CLI
@beignet/cli creates Beignet apps, generates feature slices,
inspects route wiring, and catches drift after manual edits.
Most users start with these commands:
bunx -p @beignet/cli beignet create my-app
cd my-app
bunx -p @beignet/cli beignet make resource projects
bunx -p @beignet/cli beignet routes
bunx -p @beignet/cli beignet lint
bunx -p @beignet/cli beignet doctor
create starts the app. make resource adds a full contract-to-test feature
slice. routes shows what the CLI can inspect. lint checks dependency
direction. doctor catches drift after manual edits.
Create a new app
bunx -p @beignet/cli beignet create my-app
By default create uses the standard preset:
- One todos contract
- App-owned ports and in-memory infra adapters
- Validated application use cases
- A Beignet server wired to Next.js route handlers
- App error helpers, auth helpers, env validation, health checks, and devtools
- A typed client
- TanStack Query
- React Hook Form
- OpenAPI output
The CLI writes files only. After creation, install dependencies and run the app:
cd my-app
bun install
cp .env.example .env.local
bun run dev
Choose a starter preset
Presets describe the amount of app structure to scaffold on day one. Integrations stay separate, so you only add the services your app needs.
bunx -p @beignet/cli beignet create my-app --preset standard
| Preset | Includes |
|---|---|
minimal | Small contract/core/server/use-case loop for early adoption or experiments |
standard | Full app layout with app errors, env validation, devtools, provider wiring, health checks, typed client, React Query, React Hook Form, OpenAPI, and separated ports/ + infra/ |
Use minimal when you want the smallest useful contract/core/server loop:
bunx -p @beignet/cli beignet create my-api --preset minimal
Use standard when you want the full app layout:
bunx -p @beignet/cli beignet create my-app --preset standard
The starter includes a provider-neutral AuthPort, anonymous auth adapter,
typed UNAUTHORIZED and FORBIDDEN errors, and a request-bound authorization
gate. Use hooks for HTTP boundary authentication and use cases or policies for
business authorization.
Choose features
Features are optional capabilities layered onto the base app.
bunx -p @beignet/cli beignet create my-app \
--preset minimal \
--features client,openapi
Available features:
| Feature | Adds |
|---|---|
client | A typed Beignet HTTP client |
react-query | TanStack Query provider and contract query helpers |
forms | React Hook Form wired to the contract body schema |
openapi | /api/openapi route generated from contracts |
Choose integrations
Integrations add provider packages, setup notes, env examples, and provider wiring where the provider can run without app-specific infrastructure.
bunx -p @beignet/cli beignet create my-app \
--integrations drizzle-turso,inngest,pino
You can also repeat --integration:
bunx -p @beignet/cli beignet create my-app \
--integration inngest \
--integration resend
Selected integrations add the provider package, required peer dependencies,
.env.example, and setup notes in docs/integrations.md.
For an app with extra services, compose the preset and integrations explicitly:
bunx -p @beignet/cli beignet create my-app \
--preset standard \
--integrations drizzle-turso,inngest,resend,upstash-rate-limit
Available integrations:
| Integration | Adds |
|---|---|
better-auth | @beignet/provider-auth-better-auth, better-auth, and notes for replacing the starter's anonymous AuthPort |
drizzle-turso | @beignet/provider-drizzle-turso, @libsql/client, drizzle-orm, drizzle-kit, and persistence scaffolding |
inngest | @beignet/core/jobs, @beignet/provider-inngest, and inngest |
pino | @beignet/provider-logger-pino and pino |
resend | @beignet/provider-mail-resend and resend |
upstash-rate-limit | @beignet/provider-rate-limit-upstash, @upstash/ratelimit, and @upstash/redis |
App conventions
See App architecture for the full structure and route registration model.
Beignet CLI commands are convention-aware. routes, lint, doctor, and
generators work best in the Next.js layout created by beignet create:
features/
app/api/
server/index.ts
server/routes.ts
Route inspection supports contract-group definitions and direct
createContract({ method, path }) exports.
The standard app layout includes the files resource generators expect:
app-context.ts
infra/app-ports.ts
ports/index.ts
lib/use-case.ts
Generated apps include these files by default. If you move them, keep the same
architecture and point beignet.config.* at the new paths. The CLI uses this
shape to inspect routes, wire generated resources, and report drift safely.
beignet doctor reports when a directory does not match the app layout.
Use beignet.config.ts, beignet.config.js,
beignet.config.mjs, or beignet.config.json when your app keeps
the same architecture under different paths:
// beignet.config.ts
import { defineConfig } from "@beignet/cli/config";
export default defineConfig({
paths: {
contracts: "src/features",
features: "src/features",
routes: "src/app/api",
server: "src/core/server/index.ts",
},
});
Config values are optional overrides. Omitted paths fall back to the generated
defaults. routes, lint, doctor, and make generators all load the same resolved
config before inspecting or writing files.
Generate a contract
Use make contract when you want to start with the HTTP contract only:
beignet make contract projects
The command honors beignet.config.* path overrides and writes
features/projects/contracts.ts. It creates a self-contained contract group
with a starter list endpoint, schema, standard error response, and exported
contract list. It does not wire route handlers, use cases, ports, or OpenAPI;
use make resource when you want the full path from contract to tests.
Like make resource, make contract skips identical files and stops on
divergent files unless you pass --force.
Generate a use case
Use make use-case inside a Beignet app when you want a focused
application workflow without generating HTTP contracts or ports:
beignet make use-case projects/archive-project
The name uses feature/action format. The command writes
features/projects/use-cases/archive-project.ts and creates or updates
features/projects/use-cases/index.ts. Read-style actions that start with
get, list, find, search, or count generate .query(...); other
actions generate .command(...).
Generate a test
Use make test after generating a use case:
beignet make use-case projects/archive-project
beignet make test projects/archive-project
The command writes features/projects/tests/archive-project.test.ts, builds the
app context through createUseCaseTester, and asserts the starter
{ ok: true } response. It also adds a test script when the app does not
already define one. Treat the output as a compiling starting point: replace the
input, context setup, and assertion with behavior-specific coverage as the use
case grows.
Generate events, jobs, listeners, and schedules
Use feature artifact generators when a workflow needs facts, background work, event reactions, or cron-triggered work:
beignet make event posts/published
beignet make job posts/send-published-email
beignet make listener posts/enqueue-published-email --event posts/published
beignet make schedule posts/daily-summary --cron "0 9 * * *" --timezone America/Chicago --route
The name uses feature/name format. The commands write colocated feature files:
features/posts/domain/events/published.tsfeatures/posts/jobs/send-published-email.tsfeatures/posts/listeners/enqueue-published-email.tsfeatures/posts/schedules/daily-summary.ts
Each command creates or updates the folder's index.ts with a registry such as
postEvents, postJobs, postListeners, or postSchedules. Those registries
give provider wiring, workers, cron routes, and tests one predictable import.
make event and make listener add @beignet/core/events; make job adds
@beignet/core/jobs; make schedule adds @beignet/core/schedules; passing
--route also writes app/api/cron/<feature>/<name>/route.ts. Generated cron
routes require CRON_SECRET and record schedule start, completion, and failure
events in devtools.
make listener expects the event file to exist at the canonical generated path.
Run make event <feature>/<event> first, then generate listeners for that event.
Generate a port
Use make port inside a Beignet app when a use case needs a new
application boundary:
beignet make port email
The command writes ports/email.ts, adds the port to AppPorts, creates a
small fake adapter for tests, and wires a throwing infra stub so the app still
typechecks until you replace it with a real port adapter. The generated port
starts with a generic execute method; rename it to the domain operation your
use case needs.
Generate a policy
Use make policy when repeated authorization rules need a named home:
beignet make policy posts
The command writes features/posts/policy.ts with a definePolicy(...) starter.
Register the policy with createGate(...), install the gate as a port, and
bind it into request context so use cases can call ctx.gate.authorize(...).
Generate a port adapter
Use make adapter after generating a port when you are ready to replace the
generated throwing stub with a concrete infra implementation.
beignet make port email
beignet make adapter email
The command writes infra/email/email-adapter.ts and replaces the generated inline
infra stub with email: createEmailAdapter(). The adapter still throws by
default; replace its implementation with real infrastructure code while keeping
use cases behind the port interface. If the infra wiring was already
customized, the command stops instead of guessing.
Generate a resource
Use make resource inside a Beignet app when you want a new vertical
slice with contracts, use cases, ports, infra, feature route wiring, and tests.
beignet make resource projects
The command honors beignet.config.* path overrides and writes:
features/projects/contracts.tsfeatures/projects/use-cases/features/projects/ports.tsinfra/projects/in-memory-project-repository.tsfeatures/projects/routes.tsfeatures/projects/tests/projects.test.ts
It also updates the central route registry (server/routes.ts in generated
apps), ports/index.ts, infra/app-ports.ts, and adds a test script when the
app does not already define one. OpenAPI routes that use server.contracts or
contractsFromRoutes(routes) stay in sync automatically through route
registration. Legacy direct
createOpenAPIHandler([...]) arrays are updated conservatively when possible.
Generated resources start with a minimal name field so the app typechecks
immediately; rename the generated schemas and repository fields for your domain.
In apps created with the drizzle-turso integration, make resource also
creates infra/db/schema/projects.ts,
infra/projects/drizzle-project-repository.ts, and registers the repository in
infra/db/repositories.ts. The in-memory repository remains available as a test
fake.
Read Build your first resource for the guided version of this flow.
make resource is idempotent for unchanged generated files: repeated runs skip
identical files and still avoid duplicate wiring. If a generated file exists
with different content, the command stops unless you pass --force.
Preview writes without changing files:
beignet make resource projects --dry-run
Use JSON output when another tool needs the exact planned changes:
beignet make resource projects --dry-run --json
Inspect routes
Use routes inside an app when you want to see which contracts the CLI can
match to Next.js route handlers.
beignet routes
The table shows method, path, contract export, and the handler file. For scripts or CI jobs, use JSON:
beignet routes --json
Check for drift
Use doctor to catch common contract wiring drift:
beignet doctor
The checks detect contracts without a matching Next.js route handler, feature
route groups missing from the central route list, route handlers that do not map
to known contracts, OpenAPI drift in legacy direct arrays and common exported
contract lists, partially wired generated resource slices, provider packages without
matching canonical app ports, and generated ports without test fakes.
doctor exits non-zero for errors and also supports JSON output:
beignet doctor --json
Use --strict for CI-oriented checks that may be noisy locally, such as missing
generated resource tests. Strict mode exits non-zero for warnings as well as
errors.
beignet doctor --strict --json
Use --fix for low-risk maintenance fixes. Today it can add a missing test
script and repair legacy direct createOpenAPIHandler([...]) arrays when the
missing contracts are already imported in the OpenAPI route.
beignet doctor --fix
Lint dependency direction
Use lint when you want the CLI to enforce Beignet's architectural import
rules:
beignet lint
The command scans static imports and exits non-zero when core feature code
reaches into runtime or framework layers. It checks domain, use case, policy,
port, contract, and route files for imports from infra/, UI components,
client modules, provider packages, Next.js, React, database vendors, or other
runtime-only dependencies in the wrong direction.
Use JSON output for CI annotations or custom scripts:
beignet lint --json
Options
beignet create <directory> [options]
beignet make contract <name> [options]
beignet make event <feature>/<name> [options]
beignet make job <feature>/<name> [options]
beignet make listener <feature>/<name> --event <feature>/<event> [options]
beignet make schedule <feature>/<name> [options]
beignet make port <name> [options]
beignet make policy <name> [options]
beignet make adapter <name> [options]
beignet make resource <name> [options]
beignet make test <feature>/<action> [options]
beignet make use-case <feature>/<action> [options]
beignet routes [--json]
beignet lint [--json]
beignet doctor [--json] [--strict] [--fix]
| Option | Description |
|---|---|
--template next | Selects the app template. next is the only template today. |
--preset standard | Selects minimal or standard. Defaults to standard. |
--package-manager bun | Controls the package manager shown in next steps. |
--feature <name> | Adds one feature. Repeatable. |
--features <names> | Adds features from a comma-separated list. |
--integration <name> | Adds one integration. Repeatable. |
--integrations <names> | Adds integrations from a comma-separated list. |
--force | With create, writes into a non-empty directory. With make, overwrites generated files. |
--dry-run | Previews make writes without changing files. |
--json | Prints machine-readable output for routes, lint, doctor, or make commands. |
--event <feature>/<event> | Selects the event for make listener. |
--cron <expression> | Sets the cron expression for make schedule. Defaults to 0 9 * * *. |
--timezone <zone> | Sets the schedule timezone, for example America/Chicago. |
--route | Adds a Next.js cron route for make schedule. |
--strict | Includes CI-oriented doctor warnings and fails on warnings. |
--fix | Applies low-risk doctor fixes before reporting remaining drift. |