Skip to content

Web Integrations

Twilic ships npm packages that encode and decode binary request and response bodies for common JavaScript web stacks. All integrations use the same MIME type and @twilic/core codec.

Content type

Every package exports TWILIC_CONTENT_TYPE:

text
application/vnd.twilic

Send and expect this Content-Type header on Twilic payloads. Integrations reject or skip decoding when the header does not match (configurable with requireContentType: false on parsers).

Use stateless Dynamic or Batch profiles for HTTP request/response. Stateful session compression requires a persistent ordered channel and is not appropriate for typical HTTP APIs. See FAQ — Can I use Twilic over HTTP?.

Packages

PackageUse with
@twilic/expressExpress middleware
@twilic/fastifyFastify plugin
@twilic/honoHono middleware
@twilic/axiosAxios interceptors
@twilic/fetchfetch helpers

All packages depend on @twilic/core. Install the integration package plus its framework peer dependency.

Express

bash
pnpm add @twilic/express express @twilic/core
ts
import express from "express";
import { twilicParser, twilicSend } from "@twilic/express";

const app = express();

app.post("/users", twilicParser(), (req, res) => {
twilicSend(res, { ok: true, received: req.twilicBody });
});

Do not mount express.json() before Twilic routes on the same path, or the request body stream will already be consumed. Use a dedicated router or place twilicParser() only on routes that do not use JSON body parsing.

Fastify

bash
pnpm add @twilic/fastify fastify @twilic/core
ts
import Fastify from "fastify";
import { twilicParser, twilicPlugin, twilicReply } from "@twilic/fastify";

const app = Fastify();

await app.register(twilicPlugin);

app.post("/users", { preHandler: twilicParser() }, (request, reply) => {
return twilicReply(reply, { ok: true, received: request.twilicBody });
});

// or use the reply decorator:
app.get("/ping", (_request, reply) => reply.twilic({ pong: true }));

Hono

bash
pnpm add @twilic/hono hono @twilic/core
ts
import { Hono } from "hono";
import { twilicParser, twilicResponse } from "@twilic/hono";

const app = new Hono();

app.post("/users", twilicParser(), async (c) => {
const input = c.var.twilicBody;
return twilicResponse(c, { ok: true, received: input });
});

Axios

bash
pnpm add @twilic/axios axios @twilic/core
ts
import axios from "axios";
import { createTwilicAxios } from "@twilic/axios";

const client = createTwilicAxios(axios.create());

const { data } = await client.post("/api/users", null, {
twilicBody: { id: 1n, name: "alice" },
});

Set twilicResponse: false on a request when you expect a non-Twilic response body.

Fetch

bash
pnpm add @twilic/fetch @twilic/core
ts
import { init } from "@twilic/core";
import { twilicFetchJson } from "@twilic/fetch";

// In the browser, initialize the WASM backend once before calling helpers.
await init({ prefer: "wasm" });

const data = await twilicFetchJson("/api/users", {
method: "POST",
twilicBody: { id: 1n, name: "alice" },
});

On Node.js, @twilic/core selects the N-API backend by default and init() is still recommended before first use.

Custom codec

Each integration accepts an optional codec object with encode and decode functions. Use createTwilicExpress, createTwilicFastify, createTwilicHono, createTwilicAxios, or createTwilicFetch when you need a custom @twilic/core configuration.

Released under the CC-BY-4.0 License.