https://github.com/vcfvct/typescript-retry-decorator
lightweight typescript retry decorator with 0 dependency.
https://github.com/vcfvct/typescript-retry-decorator
annotation decorator retry typescript
Last synced: 6 months ago
JSON representation
lightweight typescript retry decorator with 0 dependency.
- Host: GitHub
- URL: https://github.com/vcfvct/typescript-retry-decorator
- Owner: vcfvct
- License: mit
- Created: 2018-11-21T22:02:15.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-07-18T14:14:25.000Z (about 1 year ago)
- Last Synced: 2025-04-01T11:51:17.434Z (6 months ago)
- Topics: annotation, decorator, retry, typescript
- Language: TypeScript
- Size: 337 KB
- Stars: 92
- Watchers: 3
- Forks: 15
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README

## A simple retry decorator for typescript with 0 dependency.
This is inspired by the [Spring-Retry project](https://github.com/spring-projects/spring-retry). Written in Typescript, *100%* Test Coverage.Import and use it. Retry for `Promise` is supported as long as the `runtime` has promise(nodejs/evergreen-browser).
### Install
> npm install typescript-retry-decoratorDepending on your setup, you may need to enable [`experimentalDecorators`](https://www.typescriptlang.org/tsconfig#experimentalDecorators) flag in `tsconfig.json`.
### Options
| Option Name | Type | Required? | Default | Description |
|:-----------------:|:------------------------:|:---------:|:---------------------------------------:|:-----------------------------------------------------------------------------------------------------------------:|
| maxAttempts | number | Yes | - | The max attempts to try |
| backOff | number | No | 0 | number in `ms` to back off. If not set, then no wait |
| backOffPolicy | enum | No | FixedBackOffPolicy | can be fixed or exponential |
| exponentialOption | object | No | `{ maxInterval: 2000, multiplier: 2 }` | This is for the `ExponentialBackOffPolicy`
The max interval each wait and the multiplier for the `backOff`. For `backoffStrategy`, more details below |
| doRetry | (e: any) => boolean | No | - | Function with error parameter to decide if repetition is necessary. |
| value | Error/Exception class | No | [ ] | An array of Exception types that are retryable. |
| useConsoleLogger | boolean | No | true | Print errors on console. |
| useOriginalError | throw original exception | No | false | `MaxAttemptsError` by default. if this is set to *true*, the `original` exception would be thrown instead. |#### Exponential options
The `exponentialOption` allows you to fine-tune the exponential backoff strategy using several options:
- `maxInterval`: The maximum interval between two retries. The default value is 2000 ms.
- `multiplier`: The multiplier to use to generate the next backoff interval from the last one. The default value is 2.
- `backoffStrategy`: Optional. If specified, determines the strategy used to introduce "jitter" between retry intervals. For an explanation of the available strategies and why you might select one over the other, check out [this article](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/).
- `ExponentialBackoffStrategy.FullJitter`: The base backoff interval is multiplied by a random number between 0 and 1.
- `ExponentialBackoffStrategy.EqualJitter`: The backoff interval is (base interval / 2) + (random value between 0 and base interval / 2).### Example
```typescript
import { Retryable, BackOffPolicy } from 'typescript-retry-decorator';let count: number = 1;
class RetryExample {
@Retryable({ maxAttempts: 3 })
static async noDelayRetry() {
console.info(`Calling noDelayRetry for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('I failed!');
}@Retryable({
maxAttempts: 3,
value: [SyntaxError, ReferenceError]
})
static async noDelaySpecificRetry(): Promise {
console.info(`Calling noDelaySpecificRetry for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new SyntaxError('I failed with SyntaxError!');
}@Retryable({
maxAttempts: 3,
backOff: 1000,
doRetry: (e: Error) => {
return e.message === 'Error: 429';
}
})
static async doRetry() {
console.info(`Calling doRetry for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('Error: 429');
}@Retryable({
maxAttempts: 3,
backOff: 1000,
doRetry: (e: Error) => {
return e.message === 'Error: 429';
}
})
static async doNotRetry() {
console.info(`Calling doNotRetry for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('Error: 404');
}@Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.FixedBackOffPolicy,
backOff: 1000
})
static async fixedBackOffRetry() {
console.info(`Calling fixedBackOffRetry 1s for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('I failed!');
}@Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.ExponentialBackOffPolicy,
backOff: 1000,
exponentialOption: { maxInterval: 4000, multiplier: 3 }
})
static async ExponentialBackOffRetry() {
console.info(`Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('I failed!');
}@Retryable({
maxAttempts: 3,
backOffPolicy: BackOffPolicy.ExponentialBackOffPolicy,
backOff: 1000,
exponentialOption: { maxInterval: 4000, multiplier: 2, backoffStrategy: ExponentialBackoffStrategy.EqualJitter }
})
static async ExponentialBackOffWithJitterRetry() {
console.info(`Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the ${count++} time at ${new Date().toLocaleTimeString()}`);
throw new Error('I failed!');
}
}(async () => {
try {
resetCount();
await RetryExample.noDelayRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}try {
resetCount();
await RetryExample.doRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}try {
resetCount();
await RetryExample.doNotRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}try {
resetCount();
await RetryExample.fixedBackOffRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}try {
resetCount();
await RetryExample.ExponentialBackOffRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}try {
resetCount();
await RetryExample.ExponentialBackOffWithJitterRetry();
} catch (e) {
console.info(`All retry done as expected, final message: '${e.message}'`);
}})();
function resetCount() {
count = 1;
}
```Run the above code with `ts-node`, then output will be:
```
Calling noDelayRetry for the 1 time at 4:12:49 PM
Calling noDelayRetry for the 2 time at 4:12:49 PM
Calling noDelayRetry for the 3 time at 4:12:49 PM
Calling noDelayRetry for the 4 time at 4:12:49 PM
I failed!
All retry done as expected, final message: 'Failed for 'noDelayRetry' for 3 times.'
Calling noDelayRetry for the 1 time at 4:12:49 PM
Calling noDelayRetry for the 2 time at 4:12:49 PM
Calling noDelayRetry for the 3 time at 4:12:49 PM
Calling noDelayRetry for the 4 time at 4:12:49 PM
I failed with SyntaxError!
All retry done as expected, final message: 'Failed for 'noDelaySpecificRetry' for 3 times.'
Calling doRetry for the 1 time at 4:12:49 PM
Calling doRetry for the 2 time at 4:12:50 PM
Calling doRetry for the 3 time at 4:12:51 PM
Calling doRetry for the 4 time at 4:12:52 PM
Error: 429
All retry done as expected, final message: 'Failed for 'doRetry' for 3 times.'
Calling doNotRetry for the 1 time at 4:12:52 PM
All retry done as expected, final message: 'Error: 404'
Calling fixedBackOffRetry 1s for the 1 time at 4:12:52 PM
Calling fixedBackOffRetry 1s for the 2 time at 4:12:53 PM
Calling fixedBackOffRetry 1s for the 3 time at 4:12:54 PM
Calling fixedBackOffRetry 1s for the 4 time at 4:12:55 PM
I failed!
All retry done as expected, final message: 'Failed for 'fixedBackOffRetry' for 3 times.'
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 1 time at 4:12:55 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 2 time at 4:12:56 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 3 time at 4:12:59 PM
Calling ExponentialBackOffRetry backOff 1s, multiplier=3 for the 4 time at 4:13:03 PM
I failed!
All retry done as expected, final message: 'Failed for 'ExponentialBackOffRetry' for 3 times.'
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 1 time at 4:13:03 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 2 time at 4:13:03 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 3 time at 4:13:05 PM
Calling ExponentialBackOffWithJitterRetry backOff 1s, multiplier=2 for the 4 time at 4:13:09 PM
I failed!
All retry done as expected, final message: 'Failed for 'ExponentialBackOffWithJitterRetry' for 3 times.'
```