Skip to content

Commit

Permalink
feat: implement 'return-fetch-json' package
Browse files Browse the repository at this point in the history
  • Loading branch information
myeongjae-kim committed Aug 5, 2023
1 parent 50a5d46 commit a75f06f
Show file tree
Hide file tree
Showing 11 changed files with 463 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public/css/main.css

# Coverage reports
coverage
**/coverage

# Dependency directory
node_modules
Expand All @@ -33,3 +34,4 @@ Thumbs.db

# Ignore built ts files
dist/**/*
**/dist/**/*
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ In implementing the `fetch` interceptors, I considered the following points:

## Installation

### Package Manager

Via npm

```bash
Expand All @@ -139,6 +141,29 @@ Via pnpm
pnpm add return-fetch
```

### \<script\> tag

```html
<!--
Pick your favourite CDN:
- https://unpkg.com/return-fetch
- https://cdn.jsdelivr.net/npm/return-fetch
- https://www.skypack.dev/view/return-fetch
- https://cdnjs.com/libraries/return-fetch
- …
-->

<!-- UMD import as window.returnFetch -->
<script src="https://unpkg.com/return-fetch"></script>

<!-- Modern import -->
<script type="module">
import returnFetch from 'https://cdn.skypack.dev/return-fetch/dist/index.js'
// ... //
</script>
```

## Demo

Run on <a href="https://stackblitz.com/edit/return-fetch" target="_blank">Stickblitz</a>.
Expand Down
12 changes: 12 additions & 0 deletions docs/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ const description = "baseURL, default headers, interceptors.";
export const metadata: Metadata = {
title,
description,
keywords: [
"fetch",
"interceptor",
"fetch-intercept",
"request",
"response",
"baseURL",
"baseUrl",
"default headers",
"header",
"nextjs",
],
icons: {
icon: [
{
Expand Down
25 changes: 25 additions & 0 deletions docs/src/app/markdowns/greetings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ In implementing the \`fetch\` interceptors, I considered the following points:
## Installation
### Package Manager
Via npm
\`\`\`bash
Expand All @@ -59,6 +61,29 @@ Via pnpm
pnpm add return-fetch
\`\`\`
### \\<script\\> tag
\`\`\`html
<!--
Pick your favourite CDN:
- https://unpkg.com/return-fetch
- https://cdn.jsdelivr.net/npm/return-fetch
- https://www.skypack.dev/view/return-fetch
- https://cdnjs.com/libraries/return-fetch
- …
-->
<!-- UMD import as window.returnFetch -->
<script src="https://unpkg.com/return-fetch"></script>
<!-- Modern import -->
<script type="module">
import returnFetch from 'https://cdn.skypack.dev/return-fetch/dist/index.js'
// ... //
</script>
\`\`\`
## Demo
Run on <a href="https://stackblitz.com/edit/return-fetch" target="_blank">Stickblitz</a>.
Expand Down
73 changes: 73 additions & 0 deletions packages/return-fetch-json/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# return-fetch-json

An extended function of [return-fetch](https://github.com/deer-develop/return-fetch) to serialize request body and
deserialize response body as json.

[See documentation](https://return-fetch.myeongjae.kim/#3-serialize-request-body-and-deserialize-response-body) or
[see demo](https://stackblitz.com/edit/return-fetch-json).

```ts
import returnFetchJson from "return-fetch-json";

// Create an extended fetch function and use it instead of the global fetch.
export const fetchExtended = returnFetchJson({
jsonParser: JSON.parse, // `jsonParser` property is omittable. You can use your custom parser.
baseUrl: "https://jsonplaceholder.typicode.com"
});

//////////////////// Use it somewhere ////////////////////
fetchExtended<{ id: number }>("/posts", {
method: "POST",
body: { message: "Hello, world!" },
}).then(it => it.body)
.then(console.log);
```

## Installation

### Package Manager

Via npm

```bash
npm install return-fetch-json
```

Via yarn

```bash
yarn add return-fetch-json
```

Via pnpm

```bash
pnpm add return-fetch-json
```

### \<script\> tag

```html
<!--
Pick your favourite CDN:
- https://unpkg.com/return-fetch-json
- https://cdn.jsdelivr.net/npm/return-fetch-json
- https://www.skypack.dev/view/return-fetch-json
- https://cdnjs.com/libraries/return-fetch-json
- …
-->

<!-- UMD import as window.returnFetchJson -->
<script src="https://unpkg.com/return-fetch-json"></script>

<!-- Modern import -->
<script type="module">
import returnFetchJson from 'https://cdn.skypack.dev/return-fetch-json/dist/index.js'
// ... //
</script>
```

## Demo

Run on <a href="https://stackblitz.com/edit/return-fetch-json" target="_blank">Stickblitz</a>.
68 changes: 68 additions & 0 deletions packages/return-fetch-json/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"name": "return-fetch-json",
"type": "module",
"version": "0.4.2",
"description": "An extended function of return-fetch ot serialize request body and deserialize response body as object.",
"source": "src/index.ts",
"exports": {
".": {
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
},
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./package.json": "./package.json"
},
"types": "dist/index.d.ts",
"main": "dist/index.cjs",
"module": "dist/index.js",
"unpkg": "dist/index.umd.js",
"files": [
"dist/**/*",
"package.json"
],
"scripts": {
"preinstall": "npx only-allow pnpm",
"clean": "rimraf dist",
"bundle": "microbundle --format modern,cjs,umd",
"typedoc": "typedoc src/index.ts --out docs/public/docs",
"build": "pnpm run clean && pnpm run bundle && sh ../../scripts/generate-cts.sh",
"dev": "microbundle watch",
"prepublishOnly": "pnpm run test && pnpm run build",
"watch": "pnpm run clean && tsc -w",
"test": "vitest run",
"test:watch": "vitest",
"coverage": "vitest run --coverage",
"prettier": "prettier . --write",
"lint": "eslint src/**",
"lint-staged": "lint-staged",
"check": "tsc --strict --noEmit --extendedDiagnostics"
},
"repository": {
"type": "git",
"url": "https://github.com/deer-develop/return-fetch/tree/main/packages/return-fetch-json"
},
"homepage": "https://return-fetch.myeongjae.kim/json",
"keywords": [
"fetch",
"interceptor",
"request",
"response",
"baseURL",
"baseUrl",
"default headers",
"header",
"nextjs"
],
"author": "Myeongjae Kim",
"license": "MIT",
"dependencies": {
"return-fetch": "^0.4.2"
}
}
78 changes: 78 additions & 0 deletions packages/return-fetch-json/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import returnFetch, {
FetchArgs,
ReturnFetchDefaultOptions,
} from "return-fetch";

export type ReturnFetchJsonDefaultOptions = ReturnFetchDefaultOptions & {
jsonParser?: typeof JSON.parse;
};

// Use as a replacer of `RequestInit`
type JsonRequestInit = Omit<NonNullable<FetchArgs[1]>, "body"> & {
body?: object;
};

// Use as a replacer of `Response`
export type ResponseGenericBody<T> = Omit<
Awaited<ReturnType<typeof fetch>>,
keyof Body | "clone"
> & {
body: T;
};

export type JsonResponse<T> = T extends object
? ResponseGenericBody<T>
: ResponseGenericBody<string>;

// this resembles the default behavior of axios json parser
// https://github.com/axios/axios/blob/21a5ad34c4a5956d81d338059ac0dd34a19ed094/lib/defaults/index.js#L25
const parseJsonSafely = (
text: string,
jsonParser = JSON.parse,
): object | string => {
try {
return jsonParser(text);
} catch (e) {
if ((e as Error).name !== "SyntaxError") {
throw e;
}

return text.trim();
}
};

// Write your own high order function to serialize request body and deserialize response body.
const returnFetchJson = (args?: ReturnFetchJsonDefaultOptions) => {
const fetch = returnFetch(args);

return async <T>(
url: FetchArgs[0],
init?: JsonRequestInit,
): Promise<JsonResponse<T>> => {
const headers = new Headers(init?.headers);
headers.get("Content-Type") ||
headers.set("Content-Type", "application/json");
headers.get("Accept") || headers.set("Accept", "application/json");

const response = await fetch(url, {
...init,
headers,
body: init?.body && JSON.stringify(init.body),
});

const body = parseJsonSafely(await response.text(), args?.jsonParser) as T;

return {
headers: response.headers,
ok: response.ok,
redirected: response.redirected,
status: response.status,
statusText: response.statusText,
type: response.type,
url: response.url,
body,
} as JsonResponse<T>;
};
};

export default returnFetchJson;
Loading

0 comments on commit a75f06f

Please sign in to comment.