Restaq

Installation

Set up Restaq step by step, from install to your first mounted webhook route

You'll need Node >=22 and TypeScript >=5.

1. Install the package

pnpm add restaq

restaq includes the engine, framework adapters (restaq/next-js, restaq/express, restaq/hono, restaq/nestjs), and the relay CLI — this guide adds a database as you go. No separate CLI install, ever — see CLI for the full command reference, including a zero-install npx restaq@latest init path if you don't even have a project yet.

2. Set environment variables

Create a .env file in the root of your project and add the following environment variable:

.env
RESTAQ_BASE_URL=http://localhost:3000 # Base URL of your app

RESTAQ_BASE_URL is how the CLI (relay trigger, relay replay) knows where your app is running once it's mounted (step 7).

3. Create a relay instance

Create relay.ts at your project root. This file stays wiring-only — it creates the relay and nothing else. Every relay needs a database; the fastest way to get started is SQLite, which needs no separate server:

pnpm add better-sqlite3
// relay.ts
import { restaq as createRestaq } from 'restaq';
import Database from 'better-sqlite3';
import { registerHandlers } from './relay.handlers';

export const restaq = createRestaq({
  database: new Database('restaq.db'),
});

// The concrete relay type, including any typed event catalogs inferred from
// plugins you add later — exported so relay.handlers.ts can register
// handlers with full event.data typing.
export type AppRelay = typeof restaq;

registerHandlers(restaq);

Migrations apply automatically the first time this relay processes an event — nothing else to run for local development.

4. Swap in Postgres or MySQL for production

SQLite is fine for local development and small single-instance deployments, but not once you're running multiple instances. Swap in Postgres or MySQL by passing a different client — the dialect is detected automatically:

// relay.ts
import { restaq as createRestaq } from 'restaq';
import { Pool } from 'pg';
import { registerHandlers } from './relay.handlers';

export const restaq = createRestaq({
  database: new Pool({ connectionString: process.env.DATABASE_URL! }),
});

export type AppRelay = typeof restaq;

registerHandlers(restaq);

See Database for the MySQL setup and the full list of supported clients.

5. Apply migrations in production

Migrations still apply automatically outside production, but in production run them explicitly before scaling up new instances. The relay command comes bundled with restaq from step 1 — nothing else to install:

pnpm exec relay migrate

This loads your relay.ts and applies whatever schema your chosen dialect needs. It's idempotent — safe to run again after every deploy.

6. Register a handler

relay.on(...) calls don't belong in relay.ts — as an app grows, that file would turn into a dumping ground for every event's business logic. Put handlers in their own module instead:

// relay.handlers.ts
import type { AppRelay } from './relay';

export function registerHandlers(relay: AppRelay): void {
  relay.on('order.placed', async (event, ctx) => {
    const payment = await ctx.step.run('charge-payment', async () => {
      return { orderId: event.data.orderId, amount: event.data.amount };
    });

    await ctx.step.run('send-confirmation', async () => {
      ctx.log.info('order confirmed', payment);
    });
  });
}

Each ctx.step.run(...) checkpoints its result — if the handler throws and retries, a step that already completed returns its cached output instead of re-running.

7. Mount the handler

To receive real webhook deliveries, add a catch-all route on your server. restaq ships an adapter for each framework below — nothing extra to install:

// app/api/webhook/[...all]/route.ts
import { toNextJsHandler } from 'restaq/next-js';
import { restaq } from '@/relay';

export const { POST } = toNextJsHandler(restaq);

Each of these serves every plugin at /api/webhook/<provider> — e.g. /api/webhook/stripe, /api/webhook/github — determined entirely by wherever you mount the route, there's no base-path setting to configure. See Next.js, Express, Hono, or NestJS for framework-specific details (raw body handling, custom param names, management endpoints).

On something else? restaq.handler is still a plain (req: Request, ctx: { params: { all: string[] } }) => Promise<Response> function you can wire in yourself.

Optional: scaffold this with the CLI

relay init walks you through an interactive setup instead of doing steps 3 and 6 by hand — it asks for your database and any provider plugins, then generates relay.ts + relay.handlers.ts and installs exactly what you chose. Nothing pre-installed? One command:

npx restaq@latest init

Already have restaq in your project? relay is already available:

pnpm exec relay init

Next steps

  • Basic usage: see Basic Usage for the everyday patterns — registering handlers for real provider events, ctx.step.run, ingesting via webhook vs. directly, and inspecting/replaying via the CLI.
  • Receive real webhooks: install a provider plugin — Stripe, GitHub, Clerk, Shopify, or Resend — only once you're ready to verify real signed deliveries.
  • Event types: learn how Typed Events work and what autocomplete you get once you add a provider plugin.
  • Durable steps: dive into Durable Steps to understand caching, retries, and audit trails.
  • Replay & restart: understand the three retry operations and when to use each.
  • CLI reference: relay trigger and relay replay read the RESTAQ_BASE_URL you set in step 2 to know where to POST — see the CLI reference for every command and env var it reads.

On this page