Git Product home page Git Product logo

fetch's Introduction

@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 node-fetch, whatwg-fetch & undici)
  • 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:

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, all modern browsers + IE 11

With Node.js you need:

  • Nothing if Node.js >= 18
  • node-fetch if Node.js < 18

Check examples/node

With IE 11 you need:

  • whatwg-fetch polyfill
  • core-js for other modern JS features like async/await
  • to transpile @tkrotoff/fetch code to ES5

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 a response property 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.

fetch's People

Contributors

tkrotoff avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.