# Neon

Neon provides serverless PostgreSQL that works with Cloudflare Workers via HTTP connections. Choose Neon when you need PostgreSQL features (JSON operators, full-text search, advanced indexing) or want database-level Row Level Security alongside the generated Hono API.

## Why Neon

- **Full PostgreSQL** — Advanced queries, joins, PostGIS, full-text search
- **Database-level security** — RLS policies enforce access even if the API is bypassed
- **Serverless** — HTTP connection mode for Cloudflare Workers
- **Neon Authorize** — JWT-based RLS via the `auth.user_id()` function
- **Better Auth integration** — Works seamlessly with the Quickback auth provider

## Configuration

```typescript title="quickback/quickback.config.ts"
import { defineConfig, defineRuntime, defineDatabase, defineAuth } from "@quickback/compiler";

export default defineConfig({
  name: "my-app",
  providers: {
    runtime: defineRuntime("cloudflare"),
    database: defineDatabase("neon", {
      connectionMode: "auto",  // detects runtime
      pooled: true,
    }),
    auth: defineAuth("better-auth"),
  },
});
```

## Connection modes

| Mode | Best for | How it works |
|------|----------|--------------|
| **HTTP** | Cloudflare Workers, edge functions | Stateless HTTP queries via `@neondatabase/serverless` |
| **WebSocket** | Node.js, Bun | Persistent WebSocket connection for lower latency |
| **Auto** (default) | Mixed environments | Detects runtime and picks the best mode |

## Environment variables

```bash
# For Cloudflare Workers
npx wrangler secret put DATABASE_URL
# Paste: postgresql://user:pwd@ep-xxx.region.neon.tech/db?sslmode=require

# For local development (.env)
DATABASE_URL=postgresql://user:pwd@ep-xxx.region.neon.tech/db?sslmode=require
```

## Migrations

Neon uses PostgreSQL migrations, distinct from D1's SQLite migrations:

```bash
quickback compile      # generates migrations under quickback/drizzle/
npm run db:migrate     # applies them
```

## Row Level Security

Quickback emits RLS policies from your `firewall` and `access` config. The Neon target uses Neon Authorize's `auth.user_id()` JWT claim function (the Supabase target uses `auth.uid()` instead — same pattern, different function).

```typescript
firewall: [
  { field: 'organizationId', equals: 'ctx.activeOrgId' },
]
```

```sql
CREATE POLICY "documents_select" ON documents FOR SELECT
USING (
  organization_id = get_active_org_id()
  AND (has_any_role(ARRAY['admin']) OR user_id = auth.user_id())
);
```

### Firewall patterns

**Organization-scoped:**

```sql
CREATE POLICY "projects_select" ON projects FOR SELECT
USING (organization_id = public.get_active_org_id());
```

**User-scoped:**

```sql
CREATE POLICY "preferences_select" ON preferences FOR SELECT
USING (user_id = auth.user_id());
```

**Public tables:**

```sql
CREATE POLICY "categories_deny_anon" ON categories FOR ALL TO anon USING (false);
CREATE POLICY "categories_all" ON categories FOR ALL TO authenticated USING (true) WITH CHECK (true);
```

### Helper functions

| Function | Purpose |
|----------|---------|
| `get_active_org_id()` | Returns the user's active organization from `user_sessions` |
| `has_any_role(roles[])` | Checks if the user has any specified role |
| `has_org_role(role)` | Checks for a single specific role |
| `is_org_member()` | Checks org membership |
| `is_owner(owner_id)` | Checks record ownership |

## Setting up Neon Authorize

1. Open **Project Settings → Authorize** in the Neon console
2. Add your JWKS URL: `https://your-api.example.com/auth/v1/.well-known/jwks.json`
3. The `auth.user_id()` function will return the authenticated user's ID from the JWT

## Generated files

```
quickback/drizzle/
├── migrations/
│   ├── 0100_create_rls_helpers.sql
│   ├── 0101_create_rls_policies.sql
│   └── 0102_create_indexes.sql
src/
├── db/schema.ts          # Drizzle schema
├── auth/schema.ts        # Better Auth schema
└── lib/neon.ts           # Connection helper
```

## When to choose Neon vs D1

| Factor | Neon | D1 |
|--------|------|-----|
| SQL dialect | PostgreSQL | SQLite |
| Security model | RLS + application-layer | Application-layer only |
| Multi-region | Built-in replication | Single region per database |
| Pricing | Free tier, then usage-based | Free tier, then usage-based |
| Best for | Complex queries, existing Postgres apps | Edge-first, simple schemas |

## See also

- [D1](/compiler/targets/hono-api/database/d1) — SQLite at the edge
- [Providers](/compiler/config/providers) — All database provider options
