Beignet
Alpha software: Beignet is experimental alpha software. The
0.0.xpackage line is for early evaluation, and APIs may change between releases while the framework settles.
A TypeScript framework for building REST applications that stay coherent as they grow.
Beignet gives the HTTP boundary, application workflows, typed clients, OpenAPI, providers, and local tooling one shared model. Contracts are the starting point, but the goal is the whole app: routes validate requests and responses, use cases own behavior, ports keep infrastructure replaceable, and devtools show what happened while the app runs.
Start an app
bunx -p @beignet/cli beignet create my-app
cd my-app
bun install
cp .env.example .env.local
bun run dev
Then inspect the starter app:
bunx -p @beignet/cli beignet routes
bunx -p @beignet/cli beignet lint
bunx -p @beignet/cli beignet doctor
bun run typecheck
routes shows the contracts Beignet can match to route handlers. lint
checks dependency direction. doctor catches route, OpenAPI, and resource
drift. typecheck proves the app still matches its inferred contracts and
ports.
What makes it different
- REST stays the public API. Beignet adds validation, inference, clients, and OpenAPI without codegen or a custom transport protocol.
- Contracts connect the stack. The same route definitions feed server validation, typed clients, React Query, forms, route inspection, and docs.
- Application code has a place. Features, use cases, policies, domain code, ports, and tests follow one default structure.
- Infrastructure stays behind ports. Providers wire databases, storage, mail, auth, queues, cache, logging, and rate limits without leaking into use cases.
- The CLI checks the app model. Generators, route inspection, architecture linting, doctor checks, OpenAPI, and devtools all reinforce the same model.
One contract, many surfaces
import { createContractGroup } from "@beignet/core/contracts";
import { z } from "zod";
const todos = createContractGroup()
.namespace("todos")
.prefix("/api/todos");
export const getTodo = todos
.get("/:id")
.pathParams(z.object({ id: z.string() }))
.responses({ 200: z.object({
id: z.string(),
title: z.string(),
completed: z.boolean(),
}) });
That contract can be registered on the server, called from a typed client, included in OpenAPI output, and reused by frontend adapters.
What to read first
- Quickstart creates and inspects a new app.
- Build your first resource follows one generated feature through contracts, use cases, ports, infra, route files, and tests.
- App architecture explains where the main parts of the app belong.
- Contracts and Routes and server explain the HTTP boundary.
- Packages and imports maps the app concepts to
@beignet/coresubpaths, integration packages, and provider packages.