Skip to content

Commit

Permalink
feat(fetch): accept JS objects for the request body (#365)
Browse files Browse the repository at this point in the history
  • Loading branch information
enxg committed Jun 5, 2022
1 parent c6540bf commit a3eee88
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 10 deletions.
25 changes: 16 additions & 9 deletions packages/fetch/src/lib/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// <reference lib="dom" />

import { QueryError } from './QueryError';
import { FetchResultTypes } from './types';
import { FetchResultTypes, type RequestOptions } from './types';

/**
* Performs an HTTP(S) fetch
Expand All @@ -17,18 +17,19 @@ import { FetchResultTypes } from './types';
* - When using `FetchResultTypes.Text` the return type will be a `string`
* - When using `FetchResultTypes.Result` the return type will be a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) ({@link Response} in typescript)
*/

export async function fetch<R>(url: URL | string, type?: FetchResultTypes.JSON): Promise<R>;
export async function fetch<R>(url: URL | string, options: RequestInit, type?: FetchResultTypes.JSON): Promise<R>;
export async function fetch<R>(url: URL | string, options: RequestOptions, type?: FetchResultTypes.JSON): Promise<R>;
export async function fetch(url: URL | string, type: FetchResultTypes.Buffer): Promise<Buffer>;
export async function fetch(url: URL | string, options: RequestInit, type: FetchResultTypes.Buffer): Promise<Buffer>;
export async function fetch(url: URL | string, options: RequestOptions, type: FetchResultTypes.Buffer): Promise<Buffer>;
export async function fetch(url: URL | string, type: FetchResultTypes.Blob): Promise<Blob>;
export async function fetch(url: URL | string, options: RequestInit, type: FetchResultTypes.Blob): Promise<Blob>;
export async function fetch(url: URL | string, options: RequestOptions, type: FetchResultTypes.Blob): Promise<Blob>;
export async function fetch(url: URL | string, type: FetchResultTypes.Text): Promise<string>;
export async function fetch(url: URL | string, options: RequestInit, type: FetchResultTypes.Text): Promise<string>;
export async function fetch(url: URL | string, options: RequestOptions, type: FetchResultTypes.Text): Promise<string>;
export async function fetch(url: URL | string, type: FetchResultTypes.Result): Promise<Response>;
export async function fetch(url: URL | string, options: RequestInit, type: FetchResultTypes.Result): Promise<Response>;
export async function fetch<R>(url: URL | string, options: RequestInit, type: FetchResultTypes): Promise<Response | Blob | Buffer | string | R>;
export async function fetch(url: URL | string, options?: RequestInit | FetchResultTypes, type?: FetchResultTypes) {
export async function fetch(url: URL | string, options: RequestOptions, type: FetchResultTypes.Result): Promise<Response>;
export async function fetch<R>(url: URL | string, options: RequestOptions, type: FetchResultTypes): Promise<Response | Blob | Buffer | string | R>;
export async function fetch(url: URL | string, options?: RequestOptions | FetchResultTypes, type?: FetchResultTypes) {
if (typeof options === 'undefined') {
options = {};
type = FetchResultTypes.JSON;
Expand All @@ -39,10 +40,16 @@ export async function fetch(url: URL | string, options?: RequestInit | FetchResu
type = FetchResultTypes.JSON;
}

let { body } = options;

if (body && typeof body === 'object') {
body = JSON.stringify(body);
}

// Transform the URL to a String, in case an URL object was passed
const stringUrl = String(url);

const result: Response = await globalThis.fetch(stringUrl, options);
const result: Response = await globalThis.fetch(stringUrl, { ...options, body });
if (!result.ok) throw new QueryError(stringUrl, result.status, result, await result.clone().text());

switch (type) {
Expand Down
4 changes: 4 additions & 0 deletions packages/fetch/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,7 @@ export const enum FetchMediaContentTypes {
*/
XML = 'application/xml'
}

export interface RequestOptions extends Omit<RequestInit, 'body'> {
body?: BodyInit | Record<any, any>;
}
18 changes: 17 additions & 1 deletion packages/fetch/tests/fetch.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import nock from 'nock';
import { URL as NodeUrl } from 'node:url';
import { fetch, FetchMediaContentTypes, FetchResultTypes, QueryError } from '../dist';
import { fetch, FetchResultTypes, QueryError, FetchMediaContentTypes } from '../dist';

describe('fetch', () => {
let nockScopeHttp: nock.Scope;
Expand All @@ -12,6 +12,9 @@ describe('fetch', () => {
.get('/simpleget')
.times(Infinity)
.reply(200, { test: true })
.post('/simplepost', { sapphire: 'isAwesome' })
.times(Infinity)
.reply(200, { test: true })
.get('/404')
.times(Infinity)
.reply(404, { success: false });
Expand All @@ -20,6 +23,9 @@ describe('fetch', () => {
.persist()
.get('/simpleget')
.times(Infinity)
.reply(200, { test: true })
.post('/simplepost', { sapphire: 'isAwesome' })
.times(Infinity)
.reply(200, { test: true });
});

Expand Down Expand Up @@ -105,6 +111,16 @@ describe('fetch', () => {

expect(response).toStrictEqual(JSON.stringify({ test: true }));
});

test('GIVEN fetch w/ object body w/ JSON response THEN returns JSON', async () => {
const response = await fetch<{ test: boolean }>(
'http://localhost/simplepost',
{ method: 'POST', body: { sapphire: 'isAwesome' } },
FetchResultTypes.JSON
);

expect(response.test).toBe(true);
});
});

describe('Unsuccessful fetches', () => {
Expand Down

0 comments on commit a3eee88

Please sign in to comment.