Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add retry option #54

Merged
merged 7 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { createClient } from 'microcms-js-sdk'; //ES6
const client = createClient({
serviceDomain: "YOUR_DOMAIN", // YOUR_DOMAIN is the XXXX part of XXXX.microcms.io
apiKey: "YOUR_API_KEY",
// retry: true // Retry attempts up to a maximum of two times.
});
```

Expand All @@ -49,6 +50,7 @@ const { createClient } = microcms;
const client = createClient({
serviceDomain: "YOUR_DOMAIN", // YOUR_DOMAIN is the XXXX part of XXXX.microcms.io
apiKey: "YOUR_API_KEY",
// retry: true // Retry attempts up to a maximum of two times.
// customFetcher: fetch.bind(globalThis), // Provide a custom `fetch` implementation as an option
});
</script>
Expand Down
28 changes: 28 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "microcms-js-sdk",
"version": "2.3.3",
"version": "2.4.0",
"description": "JavaScript SDK Client for microCMS.",
"main": "./dist/cjs/microcms-js-sdk.js",
"module": "./dist/esm/microcms-js-sdk.js",
Expand Down Expand Up @@ -29,6 +29,7 @@
"dist"
],
"dependencies": {
"async-retry": "^1.3.3",
"cross-fetch": "^3.1.5",
"encoding": "^0.1.13",
"qs": "^6.10.1"
Expand All @@ -38,6 +39,7 @@
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@types/async-retry": "^1.4.5",
"@types/jest": "^28.1.6",
"@types/node": "^15.0.2",
"@types/qs": "^6.9.6",
Expand Down
117 changes: 79 additions & 38 deletions src/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ import {
UpdateRequest,
DeleteRequest,
} from './types';
import { API_VERSION, BASE_DOMAIN } from './utils/constants';
import {
API_VERSION,
BASE_DOMAIN,
MAX_RETRY_COUNT,
MIN_TIMEOUT_MS,
} from './utils/constants';
import { generateFetchClient } from './lib/fetch';
import retry from 'async-retry';

/**
* Initialize SDK Client
Expand All @@ -29,6 +35,7 @@ export const createClient = ({
serviceDomain,
apiKey,
customFetch,
retry: retryOption,
}: MicroCMSClient) => {
if (!serviceDomain || !apiKey) {
throw new Error('parameter is required (check serviceDomain and apiKey)');
Expand All @@ -55,53 +62,87 @@ export const createClient = ({
customBody,
}: MakeRequest) => {
const fetchClient = generateFetchClient(apiKey, customFetch);

const queryString = parseQuery(queries);
const url = `${baseUrl}/${endpoint}${contentId ? `/${contentId}` : ''}${
queryString ? `?${queryString}` : ''
}`;

try {
const response = await fetchClient(url, {
method: method || 'GET',
headers: customHeaders,
body: customBody,
});

if (!response.ok) {
const message = await (async () => {
// Enclose `response.json()` in a try since it may throw an error
// Only return the `message` if there is a `message`
try {
const { message } = await response.json();
return message ?? null;
} catch (_) {
return null;
}
})();
return Promise.reject(
new Error(
`fetch API response status: ${response.status}${
message ? `\n message is \`${message}\`` : ''
}`
)
);
const getMessageFromResponse = async (response: Response) => {
// Enclose `response.json()` in a try since it may throw an error
// Only return the `message` if there is a `message`
try {
const { message } = await response.json();
return message ?? null;
} catch (_) {
return null;
}
};

if (method === 'DELETE') return;
return await retry(
async (bail) => {
try {
const response = await fetchClient(url, {
method: method || 'GET',
headers: customHeaders,
body: customBody,
});

return response.json();
} catch (error) {
if (error.data) {
throw error.data;
}
// If a status code in the 400 range other than 429 is returned, do not retry.
if (
response.status !== 429 &&
response.status >= 400 &&
response.status < 500
) {
const message = await getMessageFromResponse(response);

if (error.response?.data) {
throw error.response.data;
}
return bail(
new Error(
`fetch API response status: ${response.status}${
message ? `\n message is \`${message}\`` : ''
}`
)
);
}

return Promise.reject(new Error(`Network Error.\n Details: ${error}`));
}
// If the response fails with any other status code, retry until the set number of attempts is reached.
if (!response.ok) {
const message = await getMessageFromResponse(response);

return Promise.reject(
new Error(
`fetch API response status: ${response.status}${
message ? `\n message is \`${message}\`` : ''
}`
)
);
}

if (method === 'DELETE') return;

return response.json();
} catch (error) {
if (error.data) {
throw error.data;
}

if (error.response?.data) {
throw error.response.data;
}

return Promise.reject(
new Error(`Network Error.\n Details: ${error}`)
);
}
},
{
retries: retryOption ? MAX_RETRY_COUNT : 0,
onRetry: (err, num) => {
console.log(err);
console.log(`Waiting for retry (${num}/${MAX_RETRY_COUNT})`);
},
minTimeout: MIN_TIMEOUT_MS,
}
Comment on lines +137 to +144
Copy link
Member Author

@Sinhalite Sinhalite Apr 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

リトライはデフォルトの設定ではOFFとしています、

Exponential Backoffのアルゴリズムについては、async-retryのReadmeに記載があります。
https://github.com/vercel/async-retry#api

こちらの設定の場合、
1回目のリトライ: 1回目のリクエスト失敗から、5秒〜10秒のディレイ
2回目のリトライ: 2回目のリクエスト失敗から、10秒〜20秒のディレイ
の間に実行されます。

);
};

/**
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface MicroCMSClient {
serviceDomain: string;
apiKey: string;
customFetch?: Fetch;
retry?: boolean;
}

type depthNumber = 1 | 2 | 3;
Expand All @@ -26,7 +27,7 @@ export interface MicroCMSQueries {
depth?: depthNumber;
ids?: string | string[];
filters?: string;
richEditorFormat?: 'html'|'object';
richEditorFormat?: 'html' | 'object';
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export const BASE_DOMAIN = 'microcms.io';
export const API_VERSION = 'v1';
export const MAX_RETRY_COUNT = 2;
export const MIN_TIMEOUT_MS = 5000;
Loading