Restaq
CLI

relay init

Interactively scaffold relay.ts and relay.handlers.ts in your project.

Walks you through an interactive setup and scaffolds relay.ts + relay.handlers.ts, installing exactly the packages your choices need.

Usage

relay init [--force] [--dir <path>]

Options

FlagDefaultDescription
--forcefalseOverwrite existing relay.ts / relay.handlers.ts
--dircurrent directoryWhere to scaffold into

What it asks

  1. App name — cosmetic, shown in the wizard's closing message
  2. Framework — skipped and auto-detected if package.json has a next dependency; otherwise choose Next.js, Express, Hono, NestJS, or a generic setup
  3. Database — SQLite (recommended to get started), Postgres, or MySQL
  4. Plugins — Stripe, GitHub, Clerk, Shopify, Resend, or none

It then detects your package manager from whatever lockfile is present (pnpm-lock.yaml, yarn.lock, bun.lockb, or falling back to npm) and installs restaq, the driver package for your chosen database, and any plugin packages you selected.

Output

Creates ./relay.handlers.ts and ./relay.ts (handlers first, since config imports it). For example, choosing SQLite and Stripe:

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

// Handlers live here, not in relay.ts - that file stays
// wiring-only (storage, plugins, retry policy), while the business logic
// that runs when an event arrives lives in its own module. For a larger
// app, split this across multiple files (e.g. one per feature) and call
// each from registerHandlers.
export function registerHandlers(relay: AppRelay): void {
  // Events from @restaq provider plugins are fully typed - relay.on()
  // autocompletes their event names and types event.data per event:
  //
  // relay.on('stripe.charge.succeeded', async (event, ctx) => {
  //   const charge = event.data.object; // a typed Stripe.Charge
  //
  //   const payment = await ctx.step.run('record-payment', async () => {
  //     return { chargeId: charge.id, amount: charge.amount };
  //   });
  //
  //   ctx.log.info('payment recorded', payment);
  // });
}
// relay.ts
import { restaq as createRestaq } from 'restaq';
import Database from 'better-sqlite3';
import { stripe } from '@restaq/stripe';
import { registerHandlers } from './relay.handlers';

export const restaq = createRestaq({
  database: new Database('restaq.db'),
  plugins: [
    stripe(), // reads STRIPE_WEBHOOK_SECRET automatically
  ],
});

// The concrete relay type, including the typed event catalog inferred from
// the plugins above - exported so relay.handlers.ts can register handlers
// with full event.data typing without re-deriving the plugins list.
export type AppRelay = typeof restaq;

registerHandlers(restaq);

If Next.js was detected (or chosen), it also creates app/api/webhook/[...all]/route.ts:

import { toNextJsHandler } from 'restaq/next-js';
import { restaq } from '../../../../relay';

export const { POST } = toNextJsHandler(restaq);

If Express, Hono, or NestJS was chosen, it creates the matching adapter file instead:

  • relay.express.ts
  • relay.hono.ts
  • relay.controller.ts

Next steps

  1. Add a relay.on(...) call inside registerHandlers() and call restaq.ingest(...) to try it.
  2. Not using one of the built-in framework adapters? Wire restaq.handler into your framework's request handling yourself — see Installation step 7.
  3. Ready to receive real webhooks? Set the matching <PROVIDER>_WEBHOOK_SECRET env var for any plugin you enabled.
  4. Ready for production? See Database to swap SQLite for Postgres or MySQL, and run relay migrate before scaling up new instances.

See the full Installation guide for more.

On this page