Introduction
Introduction
Mesh is not released yet. These pages describe Mesh 1.0.
Mesh is a TypeScript framework: you describe each thing your program stores once, in one .mesh.mx file, and Mesh writes the TypeScript you call, the checks on what callers send, the rules about who may do it, and the database tables and migrations. The whole interface is one function call, and Mesh runs on Bun.
One file, and what each part gives you
This is a complete src/domain/todo/todo.mesh.mx, top to bottom, as your editor shows it. Point at a numbered marker, or at the lines it sits on, to read what that part gives you; every part is something you would otherwise write by hand.
import { List } from "./list.mesh.mx"entity :Todo table="todos" attributes uuid :id primary-key string :title min=1 boolean :done default=false timestamp :insertedAt on=:create timestamp :updatedAt on=:update relationships belongs-to :list entity=List computed string :label() { return (&done ? "[x] " : "[ ] ") + &title } actions auto=[:read, :destroy] create :create input &title &list update :complete validate check :notDoneYet [ that=() => !&done code="already_done" message="this todo is already complete" ] do set &done=true update :rename input &title read :pending filter=() => &done === false sort asc &insertedAt policies policy :owner types=[:create, :read, :update, :destroy] authorize-if=({ actor }) => &list.ownerId === actor.id- Another entity
Listis imported from the file that declares it. The path tells Mesh which list this todo belongs to. - Name and table a name is written
:name, so this is:Todo; it lives in thetodostable. The type, the functions and the migration come from this one line. - Fields you send
titleis a string of at least one character,donea boolean that starts false. - Fields the database fills
insertedAton create,updatedAton every write. No caller sets either. - Linked to a list this creates the
listIdcolumn;&listininputis how the caller sets it.todo.listarrives when you ask. - A computed value ask for
labeland it is on the result; leave it out and it does not exist. - Calling createTodo you call
createTodo(input, context). A field it does not accept never reaches your code, and a rule about one field is one word on that field's line. - Rule and change
completeTodo({ id }, context)refuses a todo that is already done, then writes it in one turn. - One more action
renameTodo({ id, title }, context)accepts a field and changes nothing else. - A query
pendingTodo(input, context)filters in SQL, and your call can narrow it with its ownfilter. - Who may do it an action no policy covers is forbidden, and a todo in someone else's list is simply not found.
What you call
An action becomes an ordinary TypeScript function. Its name is the action name followed by the entity name: create on entity :Todo becomes createTodo, and read :pending becomes pendingTodo.
import { createTodo } from "#mesh";
const todo = await createTodo({ title: "Buy milk", list: list.id }, { actor });
There is no server, no route and no client to generate. Mesh serves a command line, a worker, a daemon or an HTTP endpoint equally, because the function is the whole interface.
The same file is built in the tutorial, and every declaration in it is in the entity reference.
You write one file per thing and edit that file. Adding a field adds a column, a type field and an input key, and one more thing to push or migrate. Removing an accepted field makes every caller that still passes it a type error.
Who it is for
You are writing TypeScript on Bun and some of your program’s data has rules attached to it: who may read it, what counts as valid, what a completed order means. Those rules usually live in a framework’s models, or in hand-written handlers with hand-written tests.
- Backend services and APIs. One declaration replaces a table definition, four DTOs, a validation layer and a controller. The HTTP layer stays yours.
- Command-line tools and workers. The action is a function. Calling it from a task is the same code a request handler runs.
- Applications with an AI agent in them. An agent editing one small declarative file, then running one command, is a much smaller job than an agent editing a model, a schema and a service layer together.
Where to go next
- Quick start — requirements, installation and a first action call.
- Working with AI agents — what an agent gets from Mesh.
- Tutorial: a todo list — two entities and everything you can do with them.
- Your first project
- Project structure — where files live and which ones you commit.
- Entities — every declaration an entity file may use.
- Using your domain — the functions Mesh generates.
- Testing — a test that runs against a real database in memory.
- Configuration — the project, its context and its adapters.
- Command line — every
meshcommand. - Customising generated code — overriding the templates Mesh builds from.