Open source

A working application, from six questions.

One command writes an API, sign-in and a React UI that already talk to each other, installs their packages and creates their databases. Sign-in, guarded routes, a sample built from screens and a no-code form designer are working before you write a line.

View on npm@xeplr/cliMIT
npx @xeplr/cli@latest new myapp

@latest makes npx fetch the newest version rather than one it cached earlier. You get two folders: api/ for the backend, sign-in and your data, and ui/ for the React frontend.

It installs and sets up the databases itself

  1. Prints what it is about to write — folders, ports, database names, settings — and asks Create it?. Answering no writes nothing.
  2. Asks Install packages and set up the databases now? (yes by default).
  3. Writes the project. It refuses a folder that already has something in it.
  4. Runs npm install in api/ and in ui/.
  5. If you gave the database connection, runs npm run setup in api/: both databases, the sign-in tables, permissions and menus.

If a step fails — Postgres not running, say — it stops, names the step, and prints the command to run again. xeplr new myapp --no-install only writes the files; then run npm install in api and ui, and npm run setup in api, yourself.

What you get

Already working, with nothing to wire up.

Sign-in, finished

Login, register, forgot and reset password, activation, profile and change password — plus the admin screens for users, roles and permissions. You write none of them.

An API that is guarded by default

Every address needs a valid token. An address you want open is named explicitly, so there is no way to leave the whole thing public by forgetting.

Tasks — a sample built from screens

A task list whose New and Edit open the task form in a popup, saving as you type into an ordinary tasks table, one column per field. Neither is written by hand: both are screens (@xeplr/ui-factory), published when the API first starts. Beside it, a panel with the Claude prompt and a Copy button.

Configure UI → Forms

For Super Admin, in the settings menu (top right) — not the side rail, which is for the pages people use. Every UI in the app: New form with a label people see and a key that names its table, design its form and its list, publish (which creates or changes the table), open it, add it to the side rail. No code.

Configure UI → Menu

Rename, reorder and hide the side rail’s items, saved for everyone. Code matches an item by its key; the label is what people read. Nobody but Super Admin sees Configure UI, and the API refuses everyone else.

Multi-tenancy, if you ask for it

Companies — or companies and workspaces, up to four levels. Every row is stamped with the one it was made in, every read sees only its rows, membership is checked on every request, and choosing one is the first screen after signing in. A “Switch company” item in the side rail (named for your outermost level) goes back to it.

A welcome screen that checks itself

It confirms you are signed in, the UI reached the API and the API accepted your token — then tells you to replace it with your own first page.

READMEs and a CLAUDE.md

READMEs at the project root and in api/ and ui/: how to start it, every command, where things live, and the rules the app follows. CLAUDE.md tells Claude how to create a new UI in this app, step by step.

No defaults, ever

Skip the connection question and the setting is left blank, with the command to fill it in written directly above it. That is deliberate. Host, port, user and password are encrypted together into one string, and a guessed value would start cleanly, migrate, serve traffic and read as empty data rather than as an error — the most expensive way for this to go wrong. Until you fill it in, the app refuses to start and names the setting. The same rule applies to every database name and storage path in a Xeplr app.

Creating a new UI

Two ways, both in the generated project.

No code

Configure UI → Forms → New form (label and key) → design → Publish → Add to menu. Publishing creates the table.

With Claude

Open Claude in the project folder and give it the prompt, changing the name and the fields. Claude follows “Create a new UI” in CLAUDE.md: the screens, server hooks and model (npx xeplr-factory screens … --no-pages), the page with its front-end hooks, the route, the side-rail item and its menu row. Then restart the sign-in service and the API — the API publishes the new screens and creates the table.

Rules the generated app follows

Architecture decisions, not defaults to tune — each one exists because the alternative failed quietly.

RuleWhy
Records live in real tables, one column per field — never JSON.select * from tasks and plain-SQL reports must work. Only a screen’s design is JSON, in the factory_screens table.
A key and a label, everywhere.A form’s key (farming_department) names its screens and table and never changes once published; a menu item’s key is what code matches. Labels are what people read, renamed any time. No label is written in code, so renaming never breaks it.
REDIS_PREFIX is required and unique per app.Two apps on one Redis under a shared prefix serve each other’s sessions, menus and permissions. Sign-in refuses to start without it, or with xeplr:.
Designing, new forms and publishing are Super Admin only.Publishing changes database tables. Every other screen route checks the permission catalog.
Rules that must hold go in server hooks.The browser is not a security boundary. Front-end hooks shape what people see and send; server hooks (api/screens/<form>/<form>.hooks.js) enforce; the model shapes the data.
Every data address is under /api.A page path and a data path must never collide.
No guessed database names or connections.A wrong-but-present value starts cleanly and reads as empty data.
Email is checked at start, never fatal.Sign-in works without email, and its banner says whether email works. Activation, invite and reset links cannot be sent until EMAIL_PROVIDER is set.

Commands

xeplr new [name] create a project, install it and set up its databases
Creates a project with an API, sign-in and a UI, all talking to each other. Asks a few questions; generates the rest.
xeplr new [name] --no-install only write the files
The same questions and the same project, without installing packages or creating databases.
xeplr encrypt redo the database connection
Turns database details into the single string the settings file holds. Run it inside a project and it finds the key and writes the result in for you. For when a password changes, the database moves, or the first answer was wrong.

Inside a project, npm run db:encrypt in api/ runs it for you. Without a project — before npm install, or from any other folder — npx @xeplr/cli encrypt asks for the key too, and prints the result rather than writing it.

Then

cd myapp/api && npm run start-auth   # sign-in
cd myapp/api && npm run start-api    # your API — publishes the sample screens on first start
cd myapp/ui  && npm run dev          # the app

The installer has already run npm install and npm run setup. Open the UI and sign in with the account you named; as Super Admin you will see Configure UI in the settings menu, top right.

Why Redis is checked at startup

Without Redis, login appears to work and then every request is refused as “Invalid or expired token”. The sign-in service checks for it at startup and refuses to start rather than let you meet that message.

# macOS
brew services start redis

# Linux
sudo systemctl start redis

# Windows — Redis has no native build, so Docker or WSL
docker run -d -p 6379:6379 redis

Commands in a generated project

From the project’s own READMEs.

WhereCommandDoes
api/npm run setupCreate both databases, the sign-in tables, permissions and menus
api/npm run start-authThe sign-in service — refuses to start without Redis or REDIS_PREFIX; its banner says whether email works
api/npm run start-apiThe API — runs migrations/ and publishes never-published screens on start
api/npm run migrate:authApply new migrations-auth/*.sql (a new menu row, say), then restart sign-in
api/npm run db:encryptRedo the encrypted database connection
api/npm run check-envCheck every required setting is present
api/npx xeplr-factory screens screens/<form>/<form>.entity.json -o screens/<form> --no-pagesA form’s screens, server hooks and model from its spec
api/npx xeplr-factory validate <screen>.jsonCheck a screen document
ui/npm run devThe app
ui/npm run buildBuild the app into dist/

Building on Xeplr?

The CLI is the fastest way in. If you would rather we built it, that is the other half of what we do.