Build scalable applications with Neon serverless Postgres and branching in Google Antigravity
# Neon Serverless Postgres for Google Antigravity
Neon provides serverless Postgres with instant branching. This guide covers patterns for Google Antigravity IDE and Gemini 3.
## Drizzle ORM Setup
```typescript
// lib/db.ts
import { neon } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
// For WebSocket connections (long-running queries)
import { Pool } from '@neondatabase/serverless';
import { drizzle as drizzleWs } from 'drizzle-orm/neon-serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const dbWs = drizzleWs(pool, { schema });
```
## Schema Definition
```typescript
// lib/schema.ts
import { pgTable, text, timestamp, integer, boolean, jsonb, uuid } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: text('email').notNull().unique(),
name: text('name').notNull(),
avatarUrl: text('avatar_url'),
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
export const posts = pgTable('posts', {
id: uuid('id').defaultRandom().primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
published: boolean('published').default(false).notNull(),
authorId: uuid('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
viewCount: integer('view_count').default(0).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
export const comments = pgTable('comments', {
id: uuid('id').defaultRandom().primaryKey(),
content: text('content').notNull(),
postId: uuid('post_id').notNull().references(() => posts.id, { onDelete: 'cascade' }),
authorId: uuid('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
// Relations
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
comments: many(comments),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.authorId], references: [users.id] }),
}));
```
## Repository Pattern
```typescript
// lib/repositories/posts.ts
import { db } from '../db';
import { posts, users, comments } from '../schema';
import { eq, desc, and, sql } from 'drizzle-orm';
export const postsRepository = {
async findAll(options: { limit?: number; offset?: number; published?: boolean } = {}) {
const { limit = 20, offset = 0, published } = options;
return db.query.posts.findMany({
where: published !== undefined ? eq(posts.published, published) : undefined,
with: { author: { columns: { id: true, name: true, avatarUrl: true } } },
orderBy: [desc(posts.createdAt)],
limit,
offset,
});
},
async findById(id: string) {
return db.query.posts.findFirst({
where: eq(posts.id, id),
with: {
author: { columns: { id: true, name: true, avatarUrl: true } },
comments: {
with: { author: { columns: { id: true, name: true, avatarUrl: true } } },
orderBy: [desc(comments.createdAt)],
},
},
});
},
async create(data: { title: string; content: string; authorId: string }) {
const [post] = await db.insert(posts).values(data).returning();
return post;
},
async update(id: string, data: Partial<{ title: string; content: string; published: boolean }>) {
const [post] = await db.update(posts)
.set({ ...data, updatedAt: new Date() })
.where(eq(posts.id, id))
.returning();
return post;
},
async incrementViewCount(id: string) {
await db.update(posts)
.set({ viewCount: sql`${posts.viewCount} + 1` })
.where(eq(posts.id, id));
},
async delete(id: string) {
await db.delete(posts).where(eq(posts.id, id));
},
async findByAuthor(authorId: string) {
return db.query.posts.findMany({
where: eq(posts.authorId, authorId),
orderBy: [desc(posts.createdAt)],
});
},
};
```
## API Routes
```typescript
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { postsRepository } from '@/lib/repositories/posts';
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
});
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = 20;
const posts = await postsRepository.findAll({
published: true,
limit,
offset: (page - 1) * limit,
});
return NextResponse.json(posts);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const result = createPostSchema.safeParse(body);
if (!result.success) {
return NextResponse.json({ error: result.error.issues }, { status: 400 });
}
const post = await postsRepository.create({
...result.data,
authorId: 'user-id-from-auth', // Get from auth
});
return NextResponse.json(post, { status: 201 });
}
```
## Migrations
```typescript
// drizzle.config.ts
import type { Config } from 'drizzle-kit';
export default {
schema: './lib/schema.ts',
out: './drizzle',
driver: 'pg',
dbCredentials: {
connectionString: process.env.DATABASE_URL!,
},
} satisfies Config;
// Run: npx drizzle-kit generate:pg
// Run: npx drizzle-kit push:pg
```
## Best Practices
1. **HTTP Driver**: Use for serverless/edge functions
2. **WebSocket**: Use Pool for long-running operations
3. **Branching**: Use Neon branches for preview deployments
4. **Connection Pooling**: Built-in connection pooling
5. **Autoscaling**: Scales to zero when not in use
6. **Type Safety**: Full TypeScript with Drizzle
Google Antigravity's Gemini 3 understands Neon patterns for serverless databases.This Neon prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their neon implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Neon projects, consider mentioning your framework version, coding style, and any specific libraries you're using.