Beignet
Beignet is experimental alpha software. The 0.0.x package line is for
early evaluation, and APIs may change between releases while the framework
settles. See stability and releases for what that means in
practice.
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
bun create beignet my-app
cd my-app
bun install
cp .env.example .env.local
bun beignet db migrate
bun run devThen open http://localhost:3000/sign-up and create the first account. The
starter is a working full-stack app: real auth pages, a todos feature, a typed
client, and a UI shell. Quickstart walks the first
session, including a first change you can watch take effect.
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 { defineContractGroup } from "@beignet/core/contracts";
import { z } from "zod";
const todos = defineContractGroup()
.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 an app and makes the first visible change.
- Mental model defines the vocabulary the rest of the docs use.
- Build your first feature generates a feature and walks one real change end to end.
- App architecture maps every folder to the decision it owns.