fetch
                                
                                
                                
                                    fetch copied to clipboard
                            
                            
                            
                        A Fetch API wrapper
@tkrotoff/fetch
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 
awaitinstead 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-fetchif Node.js >= 16.15 < 18.0 - ⚠️ node-fetch is not supported with @tkrotoff/fetch >= 0.17 due to 
Requestclass 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.