# Your first app (/docs/learn/first-app)

---
title: Your first app
description: One command scaffolds the project, and four more take it to a running admin screen.
kind: learn
package: questpie
---

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:

```bash
bunx create-questpie my-app
```

It 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 `.env` from `.env.example` with a generated database password and a
  48 character `BETTER_AUTH_SECRET`,
- installs dependencies with Bun, unless you launched it through pnpm or yarn,
- installs the `questpie` and `questpie-admin` agent 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.

<Callout type="warn" title="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.
</Callout>

## 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:

```bash
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:

```bash
cd my-app
docker compose up -d
```

The 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.

```bash
bun run db:push
```

<Callout type="warn" title="db: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`.
</Callout>

## Running it locally

The `dev` script runs Vite with `--port 3000`.

```bash
bun run dev
```

Three 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](/docs/learn/first-collection)** puts a table of
your own into that admin screen.
