UNPKG

@httpc/client

Version:

httpc client for building function-based API with minimal code and end-to-end type safety

110 lines 3.78 kB
import { fetch, Headers } from "./fetch"; export class HttpCClientError extends Error { constructor(status, body) { const message = body?.message || `[HTTP-${status}]`; super(message); this.status = status; this.body = body; } get error() { return this.body?.error; } get data() { return this.body?.data; } } export class HttpCClient { constructor(options) { this._middlewares = []; this._pipeline = this._fetch.bind(this); this.endpoint = options?.endpoint; options?.middlewares?.forEach(x => { if (typeof x === "function") { this.use(x); } else { this.use(x.key, x.value); } }); } use(key, middleware) { if (typeof key !== "string") { middleware = key; key = this._middlewares.length.toString(); } else { middleware = middleware; } const idx = this._middlewares.findIndex(x => x.key === key); if (idx >= 0) { this._middlewares[idx].value = middleware; } else { this._middlewares.push({ key, value: middleware }); } this._pipeline = this._middlewares.slice().reverse() .filter(x => !!x.value) .reduce((chain, entry) => { return request => entry.value(request, chain); }, this._fetch.bind(this)); return this; } call(operation, ...args) { return this.write(operation, ...args); } read(operation, ...args) { const query = args.length > 0 ? `?$p=${JSON.stringify(args)}` : ""; return this._send(this._createRequest("GET", `${operation}${query}`)); } write(operation, ...args) { return this._send(this._createRequest("POST", operation, args)); } async _send(request) { const response = await this._pipeline(request); let body = undefined; if (response.headers.get("content-length")) { // needed because the same response can be read multiple times body = await response.clone().json(); } if (response.status >= 400) { this._raiseHttpError(response.status, body); } return body; } _createRequest(method, pathAndQuery, data) { let endpoint = this.endpoint || ""; if (endpoint && !endpoint.startsWith("http") && !endpoint.startsWith("/")) endpoint = `https://${endpoint}`; if (endpoint && !endpoint.endsWith("/")) endpoint += "/"; let [path, qs] = pathAndQuery ? pathAndQuery.split("?") : []; if (path.startsWith("/")) path = path.substring(1); const request = { method, query: new URLSearchParams(qs), headers: new Headers(), endpoint, path, body: data, }; if (typeof data !== undefined && ["POST", "PUT", "PATCH"].includes(method)) { request.headers.set("Content-Type", "application/json"); request.body = JSON.stringify(data); } return request; } _fetch(request) { const qs = request.query.toString(); const url = `${request.endpoint ?? ""}${request.path ?? ""}${qs ? `?${qs}` : ""}`; return fetch(url, { method: request.method, headers: request.headers, body: request.body, }); } _raiseHttpError(status, body) { throw new HttpCClientError(status, body); } } //# sourceMappingURL=client.js.map