0.2.0 • Published 11 months ago
webhook-openapi v0.2.0
webhook-openapi
This library is aimed at helping to implement a Webhook server.
!WARNING This project is in the
MVP
state and the API may still change a lot. At the moment, the project fits the requirements of the project rather than general purpose
TODO:
- Fix shit-code
- Continue working and thinking about the API
- Rewrite to general-purpose usage
Usage
import { Webhook } from "webhook-openapi";
import { Type } from "@sinclair/typebox";
const WEBHOOK_URL = "http://localhost:8943";
const webhook = new Webhook()
.event(
"some",
(event) =>
event
.body(
Type.Object({
some: Type.String(),
})
)
.response(
Type.Object({
status: Type.Literal("ok"),
})
),
{
description: "Some description",
}
)
.event("some2", (event) =>
event
.body(
Type.Object({
some: Type.String(),
})
)
.response(Type.Object({}))
);
console.log(webhook.openapi); // get OpenAPI document with `webhooks` object
const response = await webhook.call(WEBHOOK_URL, "some", { some: "string" });
// ^? const response: { status: "ok" }
Plugins
Retries on timers
This simple plugin is just retries when request failed (sendError or non-ok response). First argument is timeout ms (default to 30 * 1000
).
import { retriesOnTimers } from "webhook-openapi/plugins/timers-retries";
const webhook = new Webhook()
.extend(retriesOnTimers(60 * 1000))
.event("test", (event) => event.body(Type.Object({ body: Type.String() })));
Store Drizzle
This plugin writes requests and responses to the database using drizzle
!WARNING It is important to remember that when used together with the retries plugin, only the Response with the same RequestId is duplicated
import { store } from "webhook-openapi/plugins/store-drizzle";
export type HTTPMethods =
| "GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE"
| "OPTIONS"
| "HEAD"
| "TRACE";
export const requestTable = pgTable("requests", {
id: serial("id").primaryKey(),
data: jsonb("data"),
method: text("method").$type<HTTPMethods>(),
headers: jsonb("headers").$type<Record<string, string>>(),
url: text("url"),
});
export const responseTable = pgTable("responses", {
id: serial("id").primaryKey(),
data: jsonb("data"),
headers: jsonb("headers").$type<Record<string, string>>(),
status: integer("status"),
requestId: integer("request_id")
.notNull()
.references(() => requestTable.id),
responseTime: real("response_time"),
});
const client = postgres(process.env.DATABASE_URL as string);
const db = drizzle(client);
const webhook = new Webhook()
.extend(store(db, requestTable, responseTable))
.event("test", (event) => event.body(Type.Object({ body: Type.String() })));
You own plugin
You can write your own plugin:
const retriesPlugin = new Webhook().onSendError(
({ data, request, event, webhook }) => {
setTimeout(
// @ts-expect-error
async () => webhook.call(request.url, event, data),
10 * 1000
);
}
);
const webhook = new Webhook().extend(retriesPlugin);
Hooks
- sendError
const webhook = new Webhook().onSendError(
({ data, request, event, webhook }) => {
setTimeout(
// @ts-expect-error
async () => webhook.call(request.url, event, data),
10 * 1000
);
}
);
- afterResponse
const webhook = new Webhook().onAfterResponse(
({ response, data, request, event, webhook }) => {
console.log(response);
if (!response.ok)
setTimeout(
// @ts-expect-error
async () => webhook.call(request.url, event, data),
10 * 1000
);
}
);
- beforeRequest
const webhook = new Webhook().onBeforeRequest(({ request, data }) => {
request.headers.append("x-length", JSON.stringify(data).length.toString());
request.body = JSON.stringify(data);
});
mimeType
it so boring to talk about it... Please read this test
by default application/json
and text/plain
mimeTypes are handled
import { unpack, pack } from "msgpackr";
let answer = {};
const shouldBe = { some: { values: true } };
const mimeType = "application/x-msgpack";
using server = Bun.serve({
port: 9888,
fetch: () =>
new Response(pack(shouldBe), {
headers: {
"content-type": mimeType,
},
}),
});
const webhook = new Webhook()
.mimeType(mimeType, {
serialization: (data) => pack(data),
deserialization: async (response) =>
unpack(Buffer.from(await response.arrayBuffer())),
})
.event("test", (event) => event.body(Type.Object({ body: Type.String() })))
.onAfterResponse(({ response, data }) => {
console.log(data, response);
answer = data;
});
await webhook.call(server.url.href, "test", { body: "test" });
expect(answer).toEqual(shouldBe);