Quickstart
Five minutes from nothing to a deployed app. You need Node.js 22.12 or newer and nothing else.
#1. Create an app
npx @impetik/xeer new my-app
cd my-app
npm installNothing to install first. npm install then puts Xeer in the project, so from here on npx xeer <command>
resolves the local copy — and npm run dev, npm run check, npm run build, and npm test work too,
for you and for anyone who clones the repository.
Keep the scope. A bare
npx xeerlooks for a package literally namedxeer, which is not this one, andnpx xis an unrelated package entirely. The package is@impetik/xeer; the command it provides isxeer.
Running it often enough to want it on your PATH? npm install --global @impetik/xeer, and then drop the
npx from every command below.
xeer new writes a complete, working notes app: a manifest, a typed server, a Preact client, styles,
and a passing test suite. Eleven files, all of them yours to edit.
Start from something else with --template:
npx @impetik/xeer new my-blog --template blog--template | |
|---|---|
notes (default) | Per-user notes: one table, create and delete, ownership proven in tests. |
todo | A per-user task list: add, tick off, delete, and an open count read through an index. |
blog | Posts anyone can read and only their author can delete, with a list and a read view. |
personal-site | A few static pages from one shared content module, and no database at all. |
Every template checks, tests, and builds clean, and none of them scaffolds sign-in UI — see
step 2. The rest of this page follows notes.
#2. Run it
npx xeer devThat starts a local server with your client and server running together. Client edits hot-reload;
server edits recompile and restart behind a health check. The URL is printed on startup — Xeer picks a
free port unless you pass --port.
Open it and add a note. It is already stored in a real database, and it is already scoped to you — the server filters by the caller's identity, and the database enforces it.
Notice what you did not have to do: there is no sign-in screen, and there was nothing to configure. Every caller of a Xeer app has a verified identity from their first request, so an app can own data per user without shipping any auth UI at all. Add sign-in later, when you want a named account. See Auth.
Try two browser windows side by side. Add a note in one and it appears in the other immediately: every query records which tables it read, so a write to one of them refetches exactly the queries that care. See Live updates.
To see per-user isolation for yourself, open a private window and visit the local sign-in route to become a different user:
http://127.0.0.1:5173/_xeer/auth/sign-in?persona=bob&returnTo=/Bob's list is empty, because these are two different application users. That is a local persona — the mechanism that makes authorization exercisable before you deploy anything.
#3. Change something
Open src/server.ts. Add a field to a note by declaring it in the manifest first — the manifest is
the source of truth, and the types follow from it.
In xeer.app.json, add pinned to the notes table:
{
"database": {
"tables": {
"notes": {
"fields": {
"text": { "type": "string", "maxLength": 500 },
"ownerId": { "type": "string", "maxLength": 128 },
"pinned": { "type": "boolean", "optional": true }
},
"indexes": { "by_owner": ["ownerId"] }
}
}
}
}Now ctx.db.table('notes') accepts and returns pinned in your editor, and a mutation that sets it
type-checks:
'notes.pin': mutation({
input: { id: 'string', pinned: 'boolean' },
handler: async (ctx, input) => {
const note = await ctx.db.table('notes').get(input.id);
if (!note || note.ownerId !== ctx.auth.appUserId) throw new Error('Note not found.');
return ctx.db.table('notes').update(input.id, { pinned: input.pinned });
},
}),xeer dev picks the change up as you save. If you get something wrong, the error names the file, the
line, and a suggested repair — see the diagnostics reference.
Run npx xeer check at any time for the same validation without a running server.
#4. Test it
npx xeer testxeer test builds your app, boots it with fresh isolated state that never touches your dev data, and
runs tests/*.test.ts. Every call is made as a named persona, so ownership is asserted rather than
assumed:
import { as, expect, expectFailure, test } from '@impetik/xeer/test';
test('bob cannot remove a note owned by alice', async () => {
const note = await as('alice').mutation('notes.create', { text: 'Only for alice' });
const refused = await expectFailure(as('bob').mutation('notes.remove', { id: note.id }));
expect(refused.code).toBe('operation_failed');
expect((await as('alice').query('notes.list', {})).map((row) => row.id)).toContain(note.id);
});The scaffolded suite already contains five tests like this and they pass on a fresh project. See Testing.
#5. Deploy it
npx xeer auth login # opens a browser, prints a confirmation code
npx xeer deployxeer auth login signs you in — the person deploying. It has nothing to do with your app's own users,
who need no Xeer account at all; see Auth.
xeer deploy builds a content-addressed artifact, verifies it, and ships it. When it finishes it
prints your application's URL. That URL is live on a global edge network, serving your client and your
server, with its own database and real identity for every visitor.
Deploying needs a Xeer account, and accounts are currently by invitation. Everything in steps 1 through 5 works with no account and no network — the framework, the compiler, the dev server, and the test runner are open source and run entirely on your machine. If you would like an invitation, see the FAQ.
Prefer to look before you leap:
npx xeer deploy --environment preview # a side-by-side URL; production keeps serving
npx xeer promote # make exactly that version liveThat is the whole shipping story, plus xeer rollback when you need yesterday's version back. See
Deploy, preview, promote, roll back.
#Where to go next
| If you want to… | Read |
|---|---|
| Understand every generated file | Project structure |
| Write queries, mutations, and endpoints | Server functions and the database |
| Read data and re-render | The client |
| Enforce who can see what | Auth |
| Switch users locally, and manage local data | Local development |
| Add a database table or a file store | Capabilities |
| Store an API key | Environment variables and secrets |
| Drive Xeer from an AI agent | Building with AI agents |
| Look up a command or an error code | CLI · Diagnostics |