Quickback for Hono API
Neon
Use Neon serverless PostgreSQL with Quickback. Connection modes, RLS policy generation, Neon Authorize setup, Drizzle integration.
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
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
# 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=requireMigrations
Neon uses PostgreSQL migrations, distinct from D1's SQLite migrations:
quickback compile # generates migrations under quickback/drizzle/
npm run db:migrate # applies themRow 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).
firewall: [
{ field: 'organizationId', equals: 'ctx.activeOrgId' },
]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:
CREATE POLICY "projects_select" ON projects FOR SELECT
USING (organization_id = public.get_active_org_id());User-scoped:
CREATE POLICY "preferences_select" ON preferences FOR SELECT
USING (user_id = auth.user_id());Public tables:
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
- Open Project Settings → Authorize in the Neon console
- Add your JWKS URL:
https://your-api.example.com/auth/v1/.well-known/jwks.json - 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 helperWhen 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 — SQLite at the edge
- Providers — All database provider options
- Quickback for Supabase — A different Postgres+RLS path that uses Supabase Auth instead of Better Auth