UNPKG

webhook-openapi

Version:

Helps you create you own webhook server

225 lines (177 loc) 5.74 kB
# webhook-openapi <div align="center"> [![npm](https://img.shields.io/npm/v/webhook-openapi?logo=npm&style=flat&labelColor=000&color=3b82f6)](https://www.npmjs.org/package/webhook-openapi) [![JSR](https://jsr.io/badges/webhook-openapi)](https://jsr.io/webhook-openapi) [![JSR Score](https://jsr.io/badges/webhook-openapi/score)](https://jsr.io/webhook-openapi) </div> 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 ```ts 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`). ```ts 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 ```ts 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: ```ts 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 ```ts const webhook = new Webhook().onSendError( ({ data, request, event, webhook }) => { setTimeout( // @ts-expect-error async () => webhook.call(request.url, event, data), 10 * 1000 ); } ); ``` - afterResponse ```ts 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 ```ts 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 ```ts 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); ```