@aurelle/http-fetcher
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -22,11 +22,11 @@ import { HttpFetcherError } from "src/lib/http-fetcher-error"; | ||
Accept?: string; | ||
'Accept-Charset'?: string; | ||
'Accept-Encoding'?: string; | ||
'Accept-Language'?: string; | ||
"Accept-Charset"?: string; | ||
"Accept-Encoding"?: string; | ||
"Accept-Language"?: string; | ||
Authorization?: string; | ||
'Cache-Control'?: string; | ||
"Cache-Control"?: string; | ||
Connection?: string; | ||
'Content-Length'?: string; | ||
'Content-MD5'?: string; | ||
'Content-Type'?: string; | ||
"Content-Length"?: string; | ||
"Content-MD5"?: string; | ||
"Content-Type"?: string; | ||
Date?: string; | ||
@@ -36,10 +36,10 @@ Expect?: string; | ||
Host?: string; | ||
'If-Match'?: string; | ||
'If-Modified-Since'?: string; | ||
'If-None-Match'?: string; | ||
'If-Range'?: string; | ||
'If-Unmodified-Since'?: string; | ||
'Max-Forwards'?: string; | ||
"If-Match"?: string; | ||
"If-Modified-Since"?: string; | ||
"If-None-Match"?: string; | ||
"If-Range"?: string; | ||
"If-Unmodified-Since"?: string; | ||
"Max-Forwards"?: string; | ||
Pragma?: string; | ||
'Proxy-Authorization'?: string; | ||
"Proxy-Authorization"?: string; | ||
Range?: string; | ||
@@ -49,3 +49,3 @@ Referer?: string; | ||
Upgrade?: string; | ||
'User-Agent'?: string; | ||
"User-Agent"?: string; | ||
Via?: string; | ||
@@ -52,0 +52,0 @@ Warning?: string; |
@@ -1,2 +0,4 @@ | ||
export * from './http-fetcher'; | ||
export * from './lib/http-fetcher-error'; | ||
export * from "./http-fetcher"; | ||
export * from "./lib/http-fetcher-error"; | ||
export * from "./common.types"; | ||
export * from "./http-fetcher.types"; |
@@ -44,3 +44,3 @@ var __defProp = Object.defineProperty; | ||
"Content-Type": "application/json", | ||
"Accept": "application/json" | ||
Accept: "application/json" | ||
}; | ||
@@ -79,6 +79,5 @@ | ||
const finalRequest = await this.applyRequestInterceptors(req); | ||
const init = { | ||
body: JSON.stringify(finalRequest.body), | ||
const init = __spreadValues({ | ||
headers: __spreadValues({}, finalRequest.headers) | ||
}; | ||
}, finalRequest.body && { body: JSON.stringify(finalRequest.body) }); | ||
const response = await fetch(req.fetchRequest, init); | ||
@@ -93,3 +92,7 @@ let data = null; | ||
fetchResponse: response, | ||
data: { error: { message: "Could not parse response body as JSON. http-fetcher currently does not support non-JSON outputs." } }, | ||
data: { | ||
error: { | ||
message: "Could not parse response body as JSON. http-fetcher currently does not support non-JSON outputs." | ||
} | ||
}, | ||
status: response.status | ||
@@ -96,0 +99,0 @@ } |
{ | ||
"name": "@aurelle/http-fetcher", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "A blazing fast, lightweight and easy to use HTTP client based on the fetch API.", | ||
@@ -5,0 +5,0 @@ "private": false, |
export type Promisifiable<T> = T | Promise<T>; | ||
export type Stringifiable = string | number | boolean | null | undefined; | ||
export type Stringifiable = string | number | boolean | null | undefined; |
export const JSON_HEADERS: Record<string, string> = { | ||
'Content-Type': 'application/json', | ||
'Accept': 'application/json', | ||
}; | ||
"Content-Type": "application/json", | ||
Accept: "application/json", | ||
}; |
import { JSON_HEADERS } from "src/constants"; | ||
import { HttpFetcherConfig, HttpFetcherConstructor, HttpFetcherExtension, HttpFetcherQueryParams, HttpFetcherRequest, HttpFetcherRequestInterceptor, HttpFetcherResponse, HttpFetcherResponseInterceptor } from "./http-fetcher.types"; | ||
import { | ||
HttpFetcherConfig, | ||
HttpFetcherConstructor, | ||
HttpFetcherExtension, | ||
HttpFetcherQueryParams, | ||
HttpFetcherRequest, | ||
HttpFetcherRequestInterceptor, | ||
HttpFetcherResponse, | ||
HttpFetcherResponseInterceptor, | ||
} from "./http-fetcher.types"; | ||
import { applyParams } from "src/lib/apply-params"; | ||
@@ -7,164 +16,200 @@ import { HttpFetcherError } from "src/lib/http-fetcher-error"; | ||
export class HttpFetcher { | ||
private readonly requestInterceptors: HttpFetcherRequestInterceptor<any>[] = []; | ||
private readonly responseInterceptors: HttpFetcherResponseInterceptor<any>[] = []; | ||
private readonly requestInterceptors: HttpFetcherRequestInterceptor<any>[] = | ||
[]; | ||
private readonly responseInterceptors: HttpFetcherResponseInterceptor<any>[] = | ||
[]; | ||
private async extractJson(res: Response) { | ||
return res.json(); | ||
} | ||
private async extractJson(res: Response) { | ||
return res.json(); | ||
} | ||
private async applyRequestInterceptors(req: HttpFetcherRequest): Promise<HttpFetcherRequest> { | ||
let interceptedRequest: HttpFetcherRequest = req; | ||
private async applyRequestInterceptors( | ||
req: HttpFetcherRequest | ||
): Promise<HttpFetcherRequest> { | ||
let interceptedRequest: HttpFetcherRequest = req; | ||
for (const interceptor of this.requestInterceptors) { | ||
interceptedRequest = await interceptor(interceptedRequest); | ||
} | ||
return interceptedRequest; | ||
for (const interceptor of this.requestInterceptors) { | ||
interceptedRequest = await interceptor(interceptedRequest); | ||
} | ||
private async applyResponseInterceptors(res: HttpFetcherResponse): Promise<HttpFetcherResponse> { | ||
let interceptedResponse: HttpFetcherResponse = res; | ||
return interceptedRequest; | ||
} | ||
for (const interceptor of this.responseInterceptors) { | ||
interceptedResponse = await interceptor(interceptedResponse); | ||
} | ||
private async applyResponseInterceptors( | ||
res: HttpFetcherResponse | ||
): Promise<HttpFetcherResponse> { | ||
let interceptedResponse: HttpFetcherResponse = res; | ||
return interceptedResponse; | ||
for (const interceptor of this.responseInterceptors) { | ||
interceptedResponse = await interceptor(interceptedResponse); | ||
} | ||
private wrappedFetch = async (req: HttpFetcherRequest): Promise<HttpFetcherResponse> => { | ||
req.headers = { | ||
...req.headers, | ||
...JSON_HEADERS, | ||
} | ||
return interceptedResponse; | ||
} | ||
const finalRequest = await this.applyRequestInterceptors(req); | ||
private wrappedFetch = async ( | ||
req: HttpFetcherRequest | ||
): Promise<HttpFetcherResponse> => { | ||
req.headers = { | ||
...req.headers, | ||
...JSON_HEADERS, | ||
}; | ||
const init: RequestInit = { | ||
body: JSON.stringify(finalRequest.body), | ||
headers: { | ||
...finalRequest.headers, | ||
} | ||
} | ||
const finalRequest = await this.applyRequestInterceptors(req); | ||
const response = await fetch(req.fetchRequest, init); | ||
const init: RequestInit = { | ||
headers: { | ||
...finalRequest.headers, | ||
}, | ||
...(finalRequest.body && { body: JSON.stringify(finalRequest.body) }), | ||
}; | ||
let data: any = null; | ||
const response = await fetch(req.fetchRequest, init); | ||
try { | ||
data = await this.extractJson(response); | ||
} catch (e) { | ||
throw new HttpFetcherError({ | ||
response: { | ||
request: finalRequest, | ||
fetchResponse: response, | ||
data: { error: { message: 'Could not parse response body as JSON. http-fetcher currently does not support non-JSON outputs.' } }, | ||
status: response.status, | ||
} | ||
}); | ||
} | ||
let data: any = null; | ||
const res: HttpFetcherResponse = { | ||
request: finalRequest, | ||
fetchResponse: response, | ||
data, | ||
status: response.status, | ||
} | ||
const finalResponse = await this.applyResponseInterceptors(res); | ||
try { | ||
data = await this.extractJson(response); | ||
} catch (e) { | ||
throw new HttpFetcherError({ | ||
response: { | ||
request: finalRequest, | ||
fetchResponse: response, | ||
data: { | ||
error: { | ||
message: | ||
"Could not parse response body as JSON. http-fetcher currently does not support non-JSON outputs.", | ||
}, | ||
}, | ||
status: response.status, | ||
}, | ||
}); | ||
} | ||
if (!response.ok) { | ||
throw new HttpFetcherError({ | ||
response: finalResponse, | ||
}); | ||
} | ||
const res: HttpFetcherResponse = { | ||
request: finalRequest, | ||
fetchResponse: response, | ||
data, | ||
status: response.status, | ||
}; | ||
return finalResponse; | ||
const finalResponse = await this.applyResponseInterceptors(res); | ||
if (!response.ok) { | ||
throw new HttpFetcherError({ | ||
response: finalResponse, | ||
}); | ||
} | ||
private makeUrl(url: string, params?: HttpFetcherQueryParams): string { | ||
const { baseUrl } = this.fetcherConfig; | ||
let finalUrl = url; | ||
return finalResponse; | ||
}; | ||
if (baseUrl) { | ||
finalUrl = `${baseUrl}${url}`; | ||
} | ||
private makeUrl(url: string, params?: HttpFetcherQueryParams): string { | ||
const { baseUrl } = this.fetcherConfig; | ||
let finalUrl = url; | ||
if (params) { | ||
finalUrl = applyParams(finalUrl, params); | ||
} | ||
if (baseUrl) { | ||
finalUrl = `${baseUrl}${url}`; | ||
} | ||
return finalUrl; | ||
if (params) { | ||
finalUrl = applyParams(finalUrl, params); | ||
} | ||
constructor(private readonly fetcherConfig: HttpFetcherConstructor) {} | ||
return finalUrl; | ||
} | ||
extend<T, U>(extension: HttpFetcherExtension<T, U>, context?: any): HttpFetcher { | ||
this.beforeEach(extension.beforeEach, extension ?? context); | ||
this.afterEach(extension.afterEach, extension ?? context); | ||
constructor(private readonly fetcherConfig: HttpFetcherConstructor) {} | ||
return this; | ||
} | ||
beforeEach<T>(fn: HttpFetcherRequestInterceptor<T>, context?: any): HttpFetcher { | ||
this.requestInterceptors.push(fn.bind(context)); | ||
extend<T, U>( | ||
extension: HttpFetcherExtension<T, U>, | ||
context?: any | ||
): HttpFetcher { | ||
this.beforeEach(extension.beforeEach, extension ?? context); | ||
this.afterEach(extension.afterEach, extension ?? context); | ||
return this; | ||
} | ||
return this; | ||
} | ||
afterEach<T>(fn: HttpFetcherResponseInterceptor<T>, context?: any): HttpFetcher { | ||
this.responseInterceptors.push(fn.bind(context)); | ||
return this; | ||
} | ||
beforeEach<T>( | ||
fn: HttpFetcherRequestInterceptor<T>, | ||
context?: any | ||
): HttpFetcher { | ||
this.requestInterceptors.push(fn.bind(context)); | ||
async get<ResponseType>(url: string, { headers = {}, params = {} }: HttpFetcherConfig = {}): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
} | ||
const response = await this.wrappedFetch(request); | ||
return this; | ||
} | ||
return response; | ||
} | ||
afterEach<T>( | ||
fn: HttpFetcherResponseInterceptor<T>, | ||
context?: any | ||
): HttpFetcher { | ||
this.responseInterceptors.push(fn.bind(context)); | ||
async post<ResponseType, RequestType>(url: string, body: RequestType, { headers = {}, params = {} }: HttpFetcherConfig = {}): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
body, | ||
} | ||
const response = await this.wrappedFetch(request); | ||
return this; | ||
} | ||
return response; | ||
} | ||
async get<ResponseType>( | ||
url: string, | ||
{ headers = {}, params = {} }: HttpFetcherConfig = {} | ||
): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
}; | ||
const response = await this.wrappedFetch(request); | ||
async put<ResponseType, RequestType>(url: string, body: RequestType, { headers = {}, params = {} }: HttpFetcherConfig = {}): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
body, | ||
} | ||
const response = await this.wrappedFetch(request); | ||
return response; | ||
} | ||
return response; | ||
} | ||
async post<ResponseType, RequestType>( | ||
url: string, | ||
body: RequestType, | ||
{ headers = {}, params = {} }: HttpFetcherConfig = {} | ||
): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
body, | ||
}; | ||
const response = await this.wrappedFetch(request); | ||
async delete<ResponseType>(url: string, { headers = {}, params = {} }: HttpFetcherConfig = {}): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
} | ||
const response = await this.wrappedFetch(request); | ||
return response; | ||
} | ||
return response; | ||
} | ||
} | ||
async put<ResponseType, RequestType>( | ||
url: string, | ||
body: RequestType, | ||
{ headers = {}, params = {} }: HttpFetcherConfig = {} | ||
): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
body, | ||
}; | ||
const response = await this.wrappedFetch(request); | ||
return response; | ||
} | ||
async delete<ResponseType>( | ||
url: string, | ||
{ headers = {}, params = {} }: HttpFetcherConfig = {} | ||
): Promise<HttpFetcherResponse<ResponseType>> { | ||
const fetchRequest = new Request(this.makeUrl(url, params)); | ||
const request: HttpFetcherRequest = { | ||
fetchRequest: fetchRequest, | ||
headers, | ||
params, | ||
}; | ||
const response = await this.wrappedFetch(request); | ||
return response; | ||
} | ||
} |
@@ -5,20 +5,20 @@ import { HttpFetcherError } from "src/lib/http-fetcher-error"; | ||
export interface HttpFetcherConstructor { | ||
baseUrl?: string; | ||
baseUrl?: string; | ||
} | ||
export interface HttpFetcherQueryParams { | ||
[key: string]: Stringifiable; | ||
[key: string]: Stringifiable; | ||
} | ||
export interface HttpFetcherConfig { | ||
params?: HttpFetcherQueryParams; | ||
headers?: HttpHeaders; | ||
params?: HttpFetcherQueryParams; | ||
headers?: HttpHeaders; | ||
} | ||
export interface HttpFetcherResponse<ResponseBodyType = any> { | ||
request: HttpFetcherRequest; | ||
fetchResponse: Response; | ||
data: ResponseBodyType; | ||
status: number; | ||
error?: HttpFetcherError; | ||
request: HttpFetcherRequest; | ||
fetchResponse: Response; | ||
data: ResponseBodyType; | ||
status: number; | ||
error?: HttpFetcherError; | ||
} | ||
@@ -28,11 +28,11 @@ | ||
Accept?: string; | ||
'Accept-Charset'?: string; | ||
'Accept-Encoding'?: string; | ||
'Accept-Language'?: string; | ||
"Accept-Charset"?: string; | ||
"Accept-Encoding"?: string; | ||
"Accept-Language"?: string; | ||
Authorization?: string; | ||
'Cache-Control'?: string; | ||
"Cache-Control"?: string; | ||
Connection?: string; | ||
'Content-Length'?: string; | ||
'Content-MD5'?: string; | ||
'Content-Type'?: string; | ||
"Content-Length"?: string; | ||
"Content-MD5"?: string; | ||
"Content-Type"?: string; | ||
Date?: string; | ||
@@ -42,10 +42,10 @@ Expect?: string; | ||
Host?: string; | ||
'If-Match'?: string; | ||
'If-Modified-Since'?: string; | ||
'If-None-Match'?: string; | ||
'If-Range'?: string; | ||
'If-Unmodified-Since'?: string; | ||
'Max-Forwards'?: string; | ||
"If-Match"?: string; | ||
"If-Modified-Since"?: string; | ||
"If-None-Match"?: string; | ||
"If-Range"?: string; | ||
"If-Unmodified-Since"?: string; | ||
"Max-Forwards"?: string; | ||
Pragma?: string; | ||
'Proxy-Authorization'?: string; | ||
"Proxy-Authorization"?: string; | ||
Range?: string; | ||
@@ -55,3 +55,3 @@ Referer?: string; | ||
Upgrade?: string; | ||
'User-Agent'?: string; | ||
"User-Agent"?: string; | ||
Via?: string; | ||
@@ -64,15 +64,22 @@ Warning?: string; | ||
export interface HttpFetcherRequest<RequestBodyType = any> { | ||
params: HttpFetcherQueryParams; | ||
headers: HttpHeaders; | ||
body?: RequestBodyType; | ||
fetchRequest: Request; | ||
params: HttpFetcherQueryParams; | ||
headers: HttpHeaders; | ||
body?: RequestBodyType; | ||
fetchRequest: Request; | ||
} | ||
export type HttpFetcherRequestInterceptor<RequestBodyType> = (req: HttpFetcherRequest<RequestBodyType>) => Promisifiable<HttpFetcherRequest<RequestBodyType>>; | ||
export type HttpFetcherRequestInterceptor<RequestBodyType> = ( | ||
req: HttpFetcherRequest<RequestBodyType> | ||
) => Promisifiable<HttpFetcherRequest<RequestBodyType>>; | ||
export type HttpFetcherResponseInterceptor<ResponseBodyType> = (res: HttpFetcherResponse<ResponseBodyType>) => Promisifiable<HttpFetcherResponse<ResponseBodyType>>; | ||
export type HttpFetcherResponseInterceptor<ResponseBodyType> = ( | ||
res: HttpFetcherResponse<ResponseBodyType> | ||
) => Promisifiable<HttpFetcherResponse<ResponseBodyType>>; | ||
export interface HttpFetcherExtension<ResponseBodyType = any, RequestBodyType = any> { | ||
beforeEach: HttpFetcherRequestInterceptor<RequestBodyType>; | ||
afterEach: HttpFetcherResponseInterceptor<ResponseBodyType>; | ||
} | ||
export interface HttpFetcherExtension< | ||
ResponseBodyType = any, | ||
RequestBodyType = any | ||
> { | ||
beforeEach: HttpFetcherRequestInterceptor<RequestBodyType>; | ||
afterEach: HttpFetcherResponseInterceptor<ResponseBodyType>; | ||
} |
@@ -1,2 +0,4 @@ | ||
export * from './http-fetcher'; | ||
export * from './lib/http-fetcher-error' | ||
export * from "./http-fetcher"; | ||
export * from "./lib/http-fetcher-error"; | ||
export * from "./common.types"; | ||
export * from "./http-fetcher.types"; |
@@ -1,5 +0,8 @@ | ||
export const applyHeaders = (headers: Record<string, string>, request: Request): void => { | ||
for (const [key, value] of Object.entries(headers)) { | ||
request.headers.set(key, value); | ||
} | ||
} | ||
export const applyHeaders = ( | ||
headers: Record<string, string>, | ||
request: Request | ||
): void => { | ||
for (const [key, value] of Object.entries(headers)) { | ||
request.headers.set(key, value); | ||
} | ||
}; |
import { Stringifiable } from "src/common.types"; | ||
export const applyParams = (url: string, params: Record<string, Stringifiable>): string => { | ||
const [baseUrl, q] = url.split('?'); | ||
const existingParams = new URLSearchParams(q); | ||
export const applyParams = ( | ||
url: string, | ||
params: Record<string, Stringifiable> | ||
): string => { | ||
const [baseUrl, q] = url.split("?"); | ||
const existingParams = new URLSearchParams(q); | ||
for (const [key, value] of Object.entries(params)) { | ||
existingParams.set(key, `${value}`); | ||
} | ||
for (const [key, value] of Object.entries(params)) { | ||
existingParams.set(key, `${value}`); | ||
} | ||
return `${baseUrl}?${existingParams.toString()}`; | ||
} | ||
return `${baseUrl}?${existingParams.toString()}`; | ||
}; |
import { HttpFetcherResponse } from "../http-fetcher.types"; | ||
export interface HttpFetcherErrorConfig<ResponseBodyType> { | ||
response?: HttpFetcherResponse<ResponseBodyType>; | ||
response?: HttpFetcherResponse<ResponseBodyType>; | ||
} | ||
export class HttpFetcherError<ResponseBodyType = any> extends Error { | ||
constructor(public readonly config: HttpFetcherErrorConfig<ResponseBodyType>) { | ||
let message = ''; | ||
constructor( | ||
public readonly config: HttpFetcherErrorConfig<ResponseBodyType> | ||
) { | ||
let message = ""; | ||
if (config.response) { | ||
message = `Request failed with status code ${config.response.status}`; | ||
} | ||
if (config.response) { | ||
message = `Request failed with status code ${config.response.status}`; | ||
} | ||
super(message); | ||
} | ||
super(message); | ||
} | ||
} | ||
Sorry, the diff of this file is not supported yet
47473
671