GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - tkrotoff/fetch: A small Fetch API wrapper

BranchesTags
Open more actions menu

Latest commit

 

History

335 Commits
335 Commits

Folders and files

NameName
Last commit message
Last commit date
.github/workflows
.github/workflows
 
 
.husky
.husky
 
 
.vscode
.vscode
 
 
examples
examples
 
 
src
src
 
 
.editorconfig
.editorconfig
 
 
.eslintignore
.eslintignore
 
 
.eslintrc.js
.eslintrc.js
 
 
.gitignore
.gitignore
 
 
.prettierignore
.prettierignore
 
 
CHANGELOG.md
CHANGELOG.md
 
 
JestRandomSequencer.js
JestRandomSequencer.js
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
babel.config.js
babel.config.js
 
 
jest.config.js
jest.config.js
 
 
jest.setup.ts
jest.setup.ts
 
 
package-lock.json
package-lock.json
 
 
package.json
package.json
 
 
playwright.config.ts
playwright.config.ts
 
 
prettier.config.js
prettier.config.js
 
 
tsconfig.dist.json
tsconfig.dist.json
 
 
tsconfig.json
tsconfig.json
 
 

Repository files navigation

@tkrotoff/fetch

npm version Node.js CI Test Coverage Bundle size Prettier Airbnb Code Style

A Fetch wrapper.

  • Simplifies the use of Fetch
  • Tiny: less than 200 lines of code
  • No dependencies
  • Supports Node.js & web browsers
  • Comes with test utilities
  • Fully tested (against Undici & whatwg-fetch)
  • Written in TypeScript

Why?

When using Fetch, you must write some boilerplate:

const url = 'https://example.com/profile';
const data = { username: 'example' };

try {
  const response = await fetch(url, {
    method: 'POST',
    body: JSON.stringify(data),
    headers: {
      'content-type': 'application/json'
    }
  });
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  const json = await response.json();
  console.log('Success:', json);
} catch (e) {
  console.error('Error:', e);
}

With @tkrotoff/fetch it becomes:

try {
  const response = await postJSON(url, data).json();
  console.log('Success:', response);
} catch (e /* HttpError | TypeError | DOMException */) {
  console.error('Error:', e);
}

You don't have to worry about:

  • HTTP headers: Accept and Content-Type are already set
  • stringifying the request body
  • One await instead of two
  • No need to manually throw an exception on HTTP error status (like 404 or 500)

Usage

Examples:

  • https://stackblitz.com/github/tkrotoff/fetch/tree/codesandbox.io/examples/web
  • https://stackblitz.com/github/tkrotoff/fetch/tree/codesandbox.io/examples/node
  • https://github.com/tkrotoff/MarvelHeroes

npm install @tkrotoff/fetch

import { defaults, postJSON } from '@tkrotoff/fetch';

defaults.init = { /* ... */ };

const response = await postJSON(
  'https://jsonplaceholder.typicode.com/posts',
  { title: 'foo', body: 'bar', userId: 1 }
).json();

console.log(response);

Or copy-paste Http.ts into your source code.

JavaScript runtimes support

@tkrotoff/fetch supports Node.js and modern browsers

Node.js

  • Nothing is needed if Node.js >= 18.0
  • Use --experimental-fetch if Node.js >= 16.15 < 18.0
  • ⚠️ node-fetch is not supported with @tkrotoff/fetch >= 0.17 due to Request class limitations

Check examples/node

Browsers

Check examples/web

API

  • get(input: RequestInfo | URL, init?: RequestInit): ResponsePromiseWithBodyMethods

  • post(input: RequestInfo | URL, body?: BodyInit, init?: RequestInit): ResponsePromiseWithBodyMethods

  • postJSON(input: RequestInfo | URL, body: object, init?: RequestInit): ResponsePromiseWithBodyMethods

  • put(input: RequestInfo | URL, body?: BodyInit, init?: RequestInit): ResponsePromiseWithBodyMethods

  • putJSON(input: RequestInfo | URL, body: object, init?: RequestInit): ResponsePromiseWithBodyMethods

  • patch(input: RequestInfo | URL, body?: BodyInit, init?: RequestInit): ResponsePromiseWithBodyMethods

  • patchJSON(input: RequestInfo | URL, body: object, init?: RequestInit): ResponsePromiseWithBodyMethods

  • del(input: RequestInfo | URL, init?: RequestInit): ResponsePromiseWithBodyMethods

  • isJSONResponse(response: Response): boolean

ResponsePromiseWithBodyMethods being Promise<Response> with added methods from Body.

HttpError

@tkrotoff/fetch throws HttpError with response and request properties when the HTTP status code is < 200 or >= 300.

Test utilities

  • createResponsePromise(body?: BodyInit, init?: ResponseInit): ResponsePromiseWithBodyMethods

  • createJSONResponsePromise(body: object, init?: ResponseInit): ResponsePromiseWithBodyMethods

  • createHttpError(body: BodyInit, status: number, statusText?: string): HttpError

  • createJSONHttpError(body: object, status: number, statusText?: string): HttpError

HttpStatus

Instead of writing HTTP statuses as numbers 201, 403, 503... you can replace them with HttpStatus and write more explicit code:

import { HttpStatus } from '@tkrotoff/fetch';

console.log(HttpStatus._201_Created);
console.log(HttpStatus._403_Forbidden);
console.log(HttpStatus._503_ServiceUnavailable);

type HttpStatusEnum = typeof HttpStatus[keyof typeof HttpStatus];
const status: HttpStatusEnum = HttpStatus._200_OK;

Configuration

@tkrotoff/fetch exposes defaults.init that will be applied to every request.

import { defaults } from '@tkrotoff/fetch';

defaults.init.mode = 'cors';
defaults.init.credentials = 'include';

Testing

When testing your code, use createResponsePromise() and createJSONResponsePromise():

import * as Http from '@tkrotoff/fetch';

// https://github.com/aelbore/esbuild-jest/issues/26#issuecomment-968853688
// https://github.com/swc-project/swc/issues/5059
jest.mock('@tkrotoff/fetch', () => ({
  __esModule: true,
  ...jest.requireActual('@tkrotoff/fetch')
}));

test('OK', async () => {
  const mock = jest.spyOn(Http, 'get').mockImplementation(() =>
    Http.createResponsePromise('test')
  );

  const response = await Http.get(url).text();
  expect(response).toEqual('test');

  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith(url);

  mock.mockRestore();
});

test('fail', async () => {
  const mock = jest.spyOn(Http, 'get').mockImplementation(() =>
    Http.createResponsePromise(
      '<!DOCTYPE html><title>404</title>',
      { status: 404, statusText: 'Not Found' }
    )
  );

  await expect(Http.get(url).text()).rejects.toThrow('Not Found');

  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith(url);

  mock.mockRestore();
});

Other possible syntax with jest.mock instead of jest.spyOn:

import { createResponsePromise, get } from '@tkrotoff/fetch';

beforeEach(() => jest.resetAllMocks());

jest.mock('@tkrotoff/fetch', () => ({
  ...jest.requireActual('@tkrotoff/fetch'),
  get: jest.fn(),
  post: jest.fn(),
  postJSON: jest.fn(),
  put: jest.fn(),
  putJSON: jest.fn(),
  patch: jest.fn(),
  patchJSON: jest.fn(),
  del: jest.fn()
}));

test('OK', async () => {
  jest.mocked(get).mockImplementation(() =>
    createResponsePromise('test')
  );

  const response = await get(url).text();
  expect(response).toEqual('test');

  expect(get).toHaveBeenCalledTimes(1);
  expect(get).toHaveBeenCalledWith(url);
});

test('fail', async () => {
  jest.mocked(get).mockImplementation(() =>
    createResponsePromise(
      '<!DOCTYPE html><title>404</title>',
      { status: 404, statusText: 'Not Found' }
    )
  );

  await expect(get(url).text()).rejects.toThrow('Not Found');

  expect(get).toHaveBeenCalledTimes(1);
  expect(get).toHaveBeenCalledWith(url);
});

Check examples/node and examples/web.

About

A small Fetch API wrapper

Topics

fetchfetch-apihttphttp-clienthttp-requestrequestwhatwg-fetch

Resources

Readme
MIT license
Activity

Stars

63 stars

Watchers

2 watching

Forks

10 forks
Report repository

Releases

Used by

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.