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.
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
- Prints what it is about to write — folders, ports, database names, settings — and asks
Create it?. Answering no writes nothing. - Asks
Install packages and set up the databases now?(yes by default). - Writes the project. It refuses a folder that already has something in it.
- Runs
npm installinapi/and inui/. - If you gave the database connection, runs
npm run setupinapi/: 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.
| Rule | Why |
|---|---|
| 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-installonly write the files- The same questions and the same project, without installing packages or creating databases.
xeplr encryptredo 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 appThe 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 redisCommands in a generated project
From the project’s own READMEs.
| Where | Command | Does |
|---|---|---|
api/ | npm run setup | Create both databases, the sign-in tables, permissions and menus |
api/ | npm run start-auth | The sign-in service — refuses to start without Redis or REDIS_PREFIX; its banner says whether email works |
api/ | npm run start-api | The API — runs migrations/ and publishes never-published screens on start |
api/ | npm run migrate:auth | Apply new migrations-auth/*.sql (a new menu row, say), then restart sign-in |
api/ | npm run db:encrypt | Redo the encrypted database connection |
api/ | npm run check-env | Check every required setting is present |
api/ | npx xeplr-factory screens screens/<form>/<form>.entity.json -o screens/<form> --no-pages | A form’s screens, server hooks and model from its spec |
api/ | npx xeplr-factory validate <screen>.json | Check a screen document |
ui/ | npm run dev | The app |
ui/ | npm run build | Build 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.