Skip to content

Commit

Permalink
feat: add headers option to wrapFetch function
Browse files Browse the repository at this point in the history
  • Loading branch information
jd1378 committed Jul 27, 2022
1 parent 7817c53 commit 0c8c16a
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 281 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@

# Changelog

## v5.1.0

- add support for initial headers when creating wrapper
- remove unnecessary header utils
- improve types for validator for easier header usage

## v5.0.0

- fix timeout functionality as the abort signal is now supported as of deno v1.11
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This library offers a fetch wrapper that can:
- set a validator globally or per request, to reject when validator throws.
- accept a timeout option and abort when timeout is reached
- add `Accept` header with value `application/json, text/plain, */*` if not already set by you
- set global headers when creating the wrapper

Version v5.0.0+ is the recommended version now (abort controller is used now). please don't use v4 of fetch goody anymore.

Expand Down
7 changes: 6 additions & 1 deletion extended_request_init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ interface RequestInitDiff {

export type Validator = (
response: Response,
init: ExtendedRequestInit,
init: ExtendedRequest,
) => void | Promise<void>;

export type ExtendedRequestInit =
& RequestInitDiff
& Omit<RequestInit, keyof RequestInitDiff>;

export type ExtendedRequest =
& RequestInitDiff
& Omit<RequestInit, keyof RequestInitDiff | "headers">
& { headers: Headers };
65 changes: 34 additions & 31 deletions fetch_wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as utils from "./utils.ts";
import { getHeader, setHeader } from "./header_utils.ts";
import { ExtendedRequestInit, Validator } from "./extended_request_init.ts";
import {
ExtendedRequest,
ExtendedRequestInit,
Validator,
} from "./extended_request_init.ts";

/**
* Transforms data and adds corresponding headers if possible.
Expand All @@ -17,11 +20,7 @@ function transformData(
| ReadableStream<Uint8Array>
| null
| undefined,
headers:
| Headers
| string[][]
| Record<string, string>
| undefined,
headers: Headers,
init?: RequestInit | ExtendedRequestInit,
):
| string
Expand All @@ -47,30 +46,21 @@ function transformData(
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setHeader(
headers,
headers.set(
"Content-Type",
"application/x-www-form-urlencoded;charset=utf-8",
);
return data.toString();
}
if (utils.isObject(data)) {
setHeader(
headers,
"Content-Type",
"application/json;charset=utf-8",
);
headers.set("Content-Type", "application/json;charset=utf-8");
if (init && !init.method) {
init.method = "POST";
}
return JSON.stringify(data);
}
// the default header if type undefined
setHeader(
headers,
"Content-Type",
"application/x-www-form-urlencoded",
);
headers.set("Content-Type", "application/x-www-form-urlencoded");
return data;
}

Expand All @@ -83,6 +73,8 @@ export type WrapFetchOptions = {
validator?: Validator;
/** if set, all requests will timeout after this amount of milliseconds passed */
timeout?: number;
/** if set, will be used as default headers. new added headers will be added on top of these. */
headers?: Headers;
};

export function wrapFetch(options?: WrapFetchOptions) {
Expand All @@ -91,6 +83,7 @@ export function wrapFetch(options?: WrapFetchOptions) {
userAgent,
validator,
timeout = 99999999,
headers,
} = options || {};

return async function wrappedFetch(
Expand All @@ -103,25 +96,32 @@ export function wrapFetch(options?: WrapFetchOptions) {
}

const interceptedInit = init || {};
if (!interceptedInit.headers) {
interceptedInit.headers = new Headers();

if (!(interceptedInit.headers instanceof Headers)) {
interceptedInit.headers = new Headers(interceptedInit.headers);
}

{
const baseHeaders = new Headers(headers);

for (const header of interceptedInit.headers) {
baseHeaders.set(header[0], header[1]);
}

interceptedInit.headers = baseHeaders;
}

// setup a default accept
if (!getHeader(interceptedInit.headers, "Accept")) {
setHeader(
interceptedInit.headers,
if (!interceptedInit.headers.get("Accept")) {
interceptedInit.headers.set(
"Accept",
"application/json, text/plain, */*",
);
}

// setup user agent if set
if (userAgent) {
setHeader(
interceptedInit.headers,
"User-Agent",
userAgent,
);
interceptedInit.headers.set("User-Agent", userAgent);
}

if ("form" in interceptedInit && interceptedInit.form) {
Expand Down Expand Up @@ -215,14 +215,17 @@ export function wrapFetch(options?: WrapFetchOptions) {
clearTimeout(timeoutId);

if (typeof validator === "function") {
await validator(response, interceptedInit);
await validator(response, interceptedInit as ExtendedRequest);
}

if (
"validator" in interceptedInit &&
typeof interceptedInit.validator === "function"
) {
await interceptedInit.validator(response, interceptedInit);
await interceptedInit.validator(
response,
interceptedInit as ExtendedRequest,
);
}

return response;
Expand Down
42 changes: 42 additions & 0 deletions fetch_wrapper_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,48 @@ Deno.test("interaction with a server", {
},
);

await t.step(
"WrappedFetch sends the given headers when creating wrapper",
async () => {
const headers = new Headers();
headers.set("test", "foo");
headers.set("bar", "baz");
const wrappedFetch = wrapFetch({
headers,
});

let headerString = await wrappedFetch(serverOneUrl + "/test").then(
(r) => r.text(),
);
assertStrictEquals(headerString, "foo");

headerString = await wrappedFetch(serverOneUrl + "/bar").then(
(r) => r.text(),
);
assertStrictEquals(headerString, "baz");
},
);

await t.step(
"WrappedFetch doesnt override the user's newly given headers",
async () => {
const headers = new Headers();
headers.set("test", "foo");
const wrappedFetch = wrapFetch({
headers,
});

const headerString = await wrappedFetch(serverOneUrl + "/test", {
headers: {
"test": "baz",
},
}).then(
(r) => r.text(),
);
assertStrictEquals(headerString, "baz");
},
);

await t.step(
"WrappedFetch sends FormData when formData is defined",
async () => {
Expand Down
137 changes: 0 additions & 137 deletions header_utils.ts

This file was deleted.

Loading

0 comments on commit 0c8c16a

Please sign in to comment.