@httpc/client
Version:
httpc client for building function-based API with minimal code and end-to-end type safety
110 lines • 3.78 kB
JavaScript
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