0.2.0 • Published 11 months ago

webhook-openapi v0.2.0

Weekly downloads
-
License
-
Repository
-
Last release
11 months ago

webhook-openapi

npm JSR JSR Score

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);
0.2.0

11 months ago

0.1.0

11 months ago

0.0.77

1 year ago

0.0.76

1 year ago

0.0.6

1 year ago

0.0.5

1 year ago

0.0.4

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago

0.0.1

1 year ago