Quickstart
In five minutes: write a small backend, see its worst-case usage, add an example that doubles as a test, and serve it locally.
Install
Aleph is two npm packages: the SDK you write apps with, and the CLI that checks, tests, and serves them. The CLI brings the WebAssembly build of the core with it, so there's nothing else to install.
npm install @aleph-lang/sdk
npm install --save-dev @aleph-lang/cliAleph apps are ES modules. New npm projects say "type": "commonjs" in package.json, so switch yours to modules:
npm pkg set type=moduleRequirements:
- Node.js 24 or newer. The CLI loads
app.tswith Node's built-in TypeScript support, so there's no separate build step. - TypeScript 5.8 or newer for editor support. The SDK's types do much of the checking as you type.
From the repository
git clone https://github.com/gubkin-labs/aleph.git
cd aleph
pnpm install
pnpm buildThe aleph command is then available from packages/cli (pnpm --filter @aleph-lang/cli exec aleph --help), and the example apps under examples/ use the packages directly. Building from source also needs a Rust toolchain with the wasm32-unknown-unknown target and wasm-bindgen-cli; see Contributing.
Check your setup
npx aleph --help1. Describe the app
Create app.ts:
import { app, err, t } from "@aleph-lang/sdk";
const a = app("notes");
const Note = a.entity("Note", {
key: ["ownerId", "noteId"],
fields: {
ownerId: t.string().max(64),
noteId: t.id().generated(),
text: t.string().min(1).max(500),
pinned: t.bool().default(false),
},
});
a.route("POST /notes")
.body({ text: t.string().min(1).max(500) })
.status(201)
.example({
name: "creates a note",
as: { userId: "ada" },
request: { body: { text: " hello " } },
expect: { status: 201, body: { text: "hello", pinned: false } },
})
.handle(($, { body }) => Note.insert({ ownerId: $.caller, text: body.text.trim() }));
a.route("GET /notes")
.query({ cursor: t.cursor() })
.handle(($, { query }) => Note.page({ prefix: [$.caller], limit: 20, after: query.cursor }));
a.route("POST /notes/:noteId/pin")
.params({ noteId: t.id() })
.errors({ missing: err.notFound("No such note") })
.handle(($, { params, errors }) =>
Note.getOrFail({ ownerId: $.caller, noteId: params.noteId }, errors.missing).update({ pinned: true }),
);
export default a;Routes default to signed-in callers, and $.caller is the user's id. Every string has a maximum length: that's what lets Aleph bound the size of a row.
2. Check it
npx aleph check app.ts✔ valid. The most each route and handler can use in one run:
route reads writes ops bytes read effects calls memory
limit 250 50 2,000 2 MB 50 3 8 MB
get_notes 20 8% 0 – 6 <1% 67.6 KB 3% 0 – 0 – 285.6 KB 3%
post_notes 1 <1% 1 2% 7 <1% 3.4 KB <1% 0 – 0 – 13.7 KB <1%
post_notes_noteId_pin 2 <1% 1 2% 16 <1% 6.8 KB <1% 0 – 0 – 21.9 KB <1%Each row is the most that route can ever use in one request, whatever the input or data. The limit row shows the per-request limits, and the percentage is the share of each one: – means none, and <1% means a little. get_notes reads at most 20 rows because of its limit: 20. Try changing the GET route's limit: 20 to limit: 5000, and the check fails with a diagnostic explaining which bound to lower.
3. Test it
npx aleph test app.ts✔ post_notes: creates a note
1 passed, 0 failedEach example runs on a fresh in-memory store with fixed time and ids, and also checks that the request stayed within its static bounds. See Testing with examples.
4. Serve it
npx aleph dev app.ts --watchAuthentication is faked with headers in development:
curl -X POST localhost:3000/notes -H 'x-aleph-user: ada' -H 'content-type: application/json' -d '{"text":"first"}'
curl localhost:3000/notes -H 'x-aleph-user: ada'Every response carries an x-aleph-usage header with what the request actually used.
5. Generate a typed client
npx aleph client app.ts -o client.tsimport { createClient } from "./client";
const api = createClient({ baseUrl: "http://127.0.0.1:3000", headers: () => ({ "x-aleph-user": "ada" }) });
const note = await api.postNotes({ text: "typed" }); // note.pinned: booleanNext
- Tutorial: a team task tracker covers orgs, roles, counters, and async work.
- How Aleph works explains what happened above.