> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cubby.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Building Database Apps

> Use SQLite by default on Cubby, or Neon Postgres on Builder and Pro

# Building Database Apps

Cubby is database-ready out of the box. **The default `cubby init` template is SQLite** on every plan — choose the **Neon Postgres** template (Builder/Pro) only when you need Postgres features. The default is SQLite, not Postgres.

<Info>
  Most first cubbies should start with SQLite. It is simple, fast, and already wired into the default `cubby init` template.
</Info>

## Choose a database path

| Path          | Best for                                                                                    | Availability    |
| ------------- | ------------------------------------------------------------------------------------------- | --------------- |
| SQLite        | personal tools, friend-group apps, prototypes, small dashboards, first cubbies              | every plan      |
| Neon Postgres | heavier relational apps, concurrent writes, Postgres-specific features, branching/snapshots | Builder and Pro |

Both paths use the same core rule: **read `DATABASE_URL` from the runtime environment**. Cubby injects it for you.

## SQLite: the default path

Run `cubby init` and choose the default Next.js + SQLite template:

```bash theme={null}
cubby init my-app
cd my-app
npm install
```

The template includes Prisma, a SQLite-ready schema, a database client, and Cubby instructions for AI tools:

```text theme={null}
my-app/
├── prisma/
│   └── schema.prisma     # SQLite datasource + your models
├── lib/
│   └── db.ts             # Prisma client singleton
├── cubby.yaml
├── CUBBY.md
└── package.json
```

Your Prisma datasource should read the database URL at runtime:

```prisma theme={null}
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
```

On Cubby, SQLite data is stored on persistent app storage. Locally, the template uses a local SQLite file so you can build quickly without running a database server.

## Define your models

Edit `prisma/schema.prisma`:

```prisma theme={null}
model Todo {
  id        String   @id @default(cuid())
  title     String
  completed Boolean  @default(false)
  userId    String
  createdAt DateTime @default(now())
}
```

### Decide data scoping before you write a schema — default to shared

Cubby's gateway already controls **who can open the app** (the owner + explicitly-shared
users). The `x-cubby-user-id` header answers *"is this person allowed in?"* — it is an
**access gate, not a partition key**. So **default to a shared dataset**: one deployment =
one shared dataset. For a separate group, deploy a separate copy.

```ts theme={null}
import { prisma } from '@/lib/db'

// Shared (default): every authorized user sees the same data.
export async function GET() {
  const todos = await prisma.todo.findMany({
    orderBy: { createdAt: 'desc' },
  })
  return Response.json(todos)
}
```

Only key rows by the viewer when the app is **explicitly per-user** (e.g. a private journal
where each person has their own isolated data). A family / team / household seed is a tell
that the data is shared — do not key it per viewer:

```ts theme={null}
import { headers } from 'next/headers'
import { prisma } from '@/lib/db'

// Per-user (opt in only when each viewer truly owns isolated data):
export async function GET() {
  const h = await headers()
  const userId = h.get('x-cubby-user-id')!
  const todos = await prisma.todo.findMany({
    where: { userId },
    orderBy: { createdAt: 'desc' },
  })
  return Response.json(todos)
}
```

Cubby auth runs before your app receives the request, so your app can trust the
Cubby-injected identity headers (`x-cubby-user-id`, `x-cubby-username`).

## Local development

**SQLite (the default template): no Docker, and no local DB is needed for frontend
work** — Cubby runs `prisma db push` for you on deploy. Just run:

```bash theme={null}
npm run dev
```

If you want a *live* local SQLite database to click through, the `data/` directory must
exist **first**, or `prisma db push` fails with
`P1003 Database does not exist`:

```bash theme={null}
mkdir -p data              # create the data/ dir (prisma db push then creates the .db file)
npx prisma db push         # now the schema applies and the SQLite file is created
npm run dev
```

Keep `DATABASE_URL` as `file:./data/app.db` — never a `../` path (it escapes the project
and breaks the sandbox).

**Neon (the `nextjs-neon` template): use `cubby dev`, which requires Docker** (it runs a
local `postgres:17-alpine`):

```bash theme={null}
cubby dev      # Docker required — local Postgres
cubby check
```

No Docker? Point `DATABASE_URL` at a real Neon branch in `.env.local` and run
`npm run dev`. See [`cubby dev`](/cli-reference/dev) for details.

Before deploying, make sure the schema is in source control and the app reads
`DATABASE_URL` from the environment instead of hardcoding a file path.

## Deploy

```bash theme={null}
cubby deploy
```

During deploy, Cubby injects the production `DATABASE_URL`, applies platform checks, builds the app, and runs it with persistent storage.

## Neon Postgres: Builder and Pro

Choose Neon Postgres when you need Postgres behavior rather than just persistence:

* concurrent writes beyond SQLite's sweet spot
* Postgres extensions or SQL features
* database branching and snapshots
* a workflow that already expects Postgres

Start with the Neon template:

```bash theme={null}
cubby init my-neon-app
# choose: Next.js + Neon Postgres
```

The template configures `cubby.yaml` for Neon:

```yaml theme={null}
name: my-neon-app
database:
  provider: neon
```

Cubby manages the Neon project/database and injects `DATABASE_URL` and `DIRECT_URL` at runtime. See [Building with Neon Postgres](/tutorials/neon-apps) for the full walkthrough.

## Secrets are not database config

Do not commit `.env` files or database URLs. For third-party keys, use Cubby secrets:

```bash theme={null}
cubby secrets set OPENAI_API_KEY --env prod   # NAME only — prompts for the value
cubby deploy
```

The default env is `local`, so pass `--env prod` for production secrets. Set them only
after your first deploy, then redeploy (or run `cubby secrets apply --env prod`).

Database URLs are platform-managed. App secrets are yours to set.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Prisma cannot find DATABASE_URL">
    Make sure your schema uses `url = env("DATABASE_URL")`. Locally, create the expected local env file or run through `cubby dev`; on Cubby, the value is injected at runtime.
  </Accordion>

  <Accordion title="My app needs Postgres-specific SQL">
    Use the Neon Postgres template and a Builder or Pro plan. SQLite is the default path, not the only path.
  </Accordion>

  <Accordion title="Data disappeared locally">
    Check whether you changed or deleted your local SQLite file. Local data is separate from Cubby runtime data.
  </Accordion>

  <Accordion title="The AI assistant wants to add auth or a database service">
    Point it at `CUBBY.md`. Cubby already provides auth headers and database injection; the app should use those platform primitives.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Build your first app" icon="rocket" href="/tutorials/first-app">
    A complete SQLite-first todo app walkthrough.
  </Card>

  <Card title="Use Neon Postgres" icon="elephant" href="/tutorials/neon-apps">
    The Builder/Pro Postgres path.
  </Card>
</CardGroup>
