Skip to content

Instantly share code, notes, and snippets.

@allenmichael
Created April 14, 2023 16:21
Show Gist options
  • Save allenmichael/7e252104a93145fd7b05aaa61fa3b7eb to your computer and use it in GitHub Desktop.
Save allenmichael/7e252104a93145fd7b05aaa61fa3b7eb to your computer and use it in GitHub Desktop.
Generic HTTP Client in Node/Browser
export interface ClientResponse {
statusCode: number,
headers: Headers,
body: any
}
export const Methods = {
GET: "GET",
POST: "POST",
PUT: "PUT",
DELETE: "DELETE",
PATCH: "PATCH"
} as const;
export type Methods = typeof Methods[keyof typeof Methods];
export class HttpClient {
public async request(
urlOptions: { path: string, method: Methods, headers?: Headers },
data?: string,
): Promise<any> {
const url = new URL(urlOptions.path);
if (data) {
if (!urlOptions.headers) {
urlOptions.headers = new Headers()
}
const contentLength = this.calculateContentLength(data);
urlOptions.headers.append("Content-Length", contentLength);
}
try {
const resp = await fetch(url.href, { headers: urlOptions.headers, method: urlOptions.method, body: data });
const statusCode = resp.status;
const headers = resp.headers;
const body = await resp.text();
if (resp.status >= 200 && resp.status <= 299) {
return { statusCode, headers, body };
} else {
throw new Error(`Request failed. status: ${statusCode}`);
}
} catch (e) {
throw e;
}
}
async requestWithParsing<Type>(
urlOptions: { path: string, method: Methods, headers?: Headers },
data?: string
): Promise<Type> {
const response = await this.request(urlOptions, data);
return this.parseResponse<Type>(response);
}
private parseResponse<Type>(response: ClientResponse) {
if ((response.statusCode >= 200 && response.statusCode <= 299) && response.body) {
return JSON.parse(response.body) as Type;
} else if (response.statusCode >= 200 && response.statusCode <= 299) {
return {} as Type;
} else {
throw new Error(`Request failed. status: ${response.statusCode}`);
}
}
private calculateContentLength(data: string) {
if (typeof window !== "undefined" &&
typeof window.Blob === "function") {
return new window.Blob([data]).size.toString();
} else {
return Buffer.byteLength(data).toString();
}
}
}
import { HttpClient, Methods } from './HttpClient'
import { ResponseUser, User, parseUser } from './User'
async function processRequest<T, R = any>(
urlOptions: { path: string, method: Methods, headers?: Headers },
parseResponse: (arg: T) => R,
data?: string
): Promise<R> {
const client = new HttpClient()
const response = await client.requestWithParsing<T>(urlOptions, data)
return parseResponse(response)
}
(async () => {
const client = new HttpClient()
const urlOptions = { path: "https://api.github.com/users/allenmichael", method: Methods.GET }
// const response = await client.request(urlOptions)
// console.log(response)
// const parsedResponse = await client.requestWithParsing<ResponseUser>(urlOptions);
// console.log(parsedResponse.node_id)
const parsedUser = await processRequest<ResponseUser, User>(urlOptions, parseUser);
console.log(parsedUser.nodeId)
})()
export interface ResponseUser {
login: string,
id: number,
"node_id": string
}
export interface User {
login: string,
id: number,
nodeId: string
}
export const parseUser = (user: ResponseUser): User => {
return {
login: user.login,
id: user.id,
nodeId: user.node_id
} as User
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment