Your first app
One command scaffolds the project, and four more take it to a running admin screen.
This page walks you through the scaffolder, the two choices that decide the shape of your project, and the run steps that follow.
Scaffolding the project
create-questpie is the scaffolder. It is its own package, so there is nothing to install first. The argument you give it becomes the directory name.
To create a project, run it with a name:
bunx create-questpie my-appIt refuses to run if my-app already exists. Otherwise, on the default
answers, it:
- copies a template and substitutes your project and database names,
- writes
.envfrom.env.examplewith a generated database password and a 48 characterBETTER_AUTH_SECRET, - installs dependencies with Bun, unless you launched it through pnpm or yarn,
- installs the
questpieandquestpie-adminagent skills into the project, - runs codegen,
- commits the result as
Initial commit from create-questpie.
Every template Dockerfile builds on oven/bun:1.3-alpine, and the local
database runs in Docker.
Answering the prompts
You get up to eight questions when stdin and stdout are both a terminal, one fewer when the name already arrived as an argument. Two of them decide what the project is.
A runtime is the server your app mounts into. Four ship as templates: TanStack Start, Next.js, Hono and Elysia. TanStack Start is the default.
A module is an optional package the scaffolder wires into
src/questpie/server/modules.ts. Four are available: admin, openapi,
workflows and mcp. The prompt pre-selects the ones that are on by default
for your runtime.
| Prompt | Default |
|---|---|
| Project name | none, required |
| Runtime | TanStack Start |
| Modules | admin and openapi on a render runtime, openapi alone on a headless one |
| Database name | derived from the project name |
| Install dependencies? | yes |
| Initialize git repository? | yes |
| Install QUESTPIE agent skills into the project? | yes |
| Run QUESTPIE codegen after installing dependencies? | yes |
The admin module needs a runtime that renders. TanStack Start and Next are the
two render runtimes. Hono and Elysia are headless, so admin is filtered out of
their module list, and asking for it by flag fails before any file is written.
The admin does not fall out of the generator
The admin screen is @questpie/admin, a module you enable. Pick Hono or
Elysia and you get the REST API and the typed client from questpie, the
OpenAPI surfaces from @questpie/openapi, and no admin UI.
Scaffolding without prompts
-y skips every question and takes the defaults. The project name then has to
be an argument, and flags override the defaults one at a time.
For example, to scaffold a headless Hono API with MCP and no git:
bunx create-questpie my-api --runtime hono --modules openapi,mcp -y --no-git| Flag | What it sets |
|---|---|
-t, --template <name>, --runtime <id> | Runtime. Default tanstack-start. |
--module <name> | One module. Repeat the flag for more. |
--modules <a,b,c> | Modules as a comma separated list. |
-y, --yes | Skip the prompts and take the defaults. |
--database <name> | Database name. |
--queue <adapter> | pg-boss, bullmq or none. Default pg-boss. |
--email <adapter> | console, smtp, resend or plunk. Default console. |
--realtime <adapter> | none, pg-notify or redis-streams. Default none. |
--kv <adapter> | memory or redis. Default memory. |
--no-install, --no-git, --no-skills, --no-generate | Skip that step. |
--continue-on-error | Keep the files when install or codegen fails. |
Starting the database
QUESTPIE stores everything in PostgreSQL. The template ships a
docker-compose.yml with a postgres:17 service on port 5432, matching the
DATABASE_URL already written into .env.
To start it, move into the project and bring the container up:
cd my-app
docker compose up -dThe compose file mounts docker/init-extensions.sql, which creates pg_trgm on
the first cluster init. questpie push never creates an extension, which is why
that file exists. questpie migrate does run
CREATE EXTENSION IF NOT EXISTS "pg_trgm" through the default Postgres search
adapter, and it fails with a named error when the role may not create
extensions. On managed Postgres, enable the extension through your provider
before you deploy.
Creating the tables
db:push runs questpie push. It diffs the schema your collections describe
against the database and applies the difference in place, with no migration
file in between.
bun run db:pushdb:push is development only
questpie push writes to the database without recording a migration, so the
migration history stops describing the schema. Never run it against production
or in deployment automation. --force only acknowledges the warning, it does
not make the command safe. For production, commit the output of bun run migrate:create and apply it with bun run migrate.
Running it locally
The dev script runs Vite with --port 3000.
bun run devThree URLs serve on that port, and each one comes from a different package:
| URL | What is there | From |
|---|---|---|
http://localhost:3000 | The starter page. Edit src/routes/index.tsx. | your app |
http://localhost:3000/admin | The admin screen. | @questpie/admin |
http://localhost:3000/api/docs | Scalar reference over the REST routes. | @questpie/openapi |
Open /admin first. No admin user exists yet, so the login page sends you
straight to /admin/setup. Give it a name, an email and a password of at least
eight characters. That account is created with the admin role, and the setup
route refuses to run again once any admin exists.
You now have a database, an API and a screen that reads them. It has one example collection in it.
Next
Add your first collection puts a table of your own into that admin screen.