Build edge applications with Turso LibSQL and embedded replicas in Google Antigravity
# Turso LibSQL Edge Database for Google Antigravity
Turso provides edge-native SQLite with global replication. This guide covers patterns for Google Antigravity IDE and Gemini 3.
## Client Setup
```typescript
// lib/db.ts
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import * as schema from './schema';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
export const db = drizzle(client, { schema });
```
## Embedded Replica
```typescript
// lib/db-replica.ts
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
import * as schema from './schema';
// Embedded replica syncs from primary
const client = createClient({
url: 'file:local.db',
syncUrl: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
syncInterval: 60, // Sync every 60 seconds
});
export const db = drizzle(client, { schema });
// Manual sync
export async function syncReplica() {
await client.sync();
}
```
## Schema with Drizzle
```typescript
// lib/schema.ts
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
import { relations, sql } from 'drizzle-orm';
export const products = sqliteTable('products', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
name: text('name').notNull(),
description: text('description'),
price: real('price').notNull(),
category: text('category').notNull(),
inStock: integer('in_stock', { mode: 'boolean' }).default(true).notNull(),
createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`).notNull(),
});
export const orders = sqliteTable('orders', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
userId: text('user_id').notNull(),
status: text('status').$type<'pending' | 'processing' | 'shipped' | 'delivered'>().default('pending').notNull(),
total: real('total').notNull(),
createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`).notNull(),
});
export const orderItems = sqliteTable('order_items', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
orderId: text('order_id').notNull().references(() => orders.id),
productId: text('product_id').notNull().references(() => products.id),
quantity: integer('quantity').notNull(),
price: real('price').notNull(),
});
export const ordersRelations = relations(orders, ({ many }) => ({
items: many(orderItems),
}));
export const orderItemsRelations = relations(orderItems, ({ one }) => ({
order: one(orders, { fields: [orderItems.orderId], references: [orders.id] }),
product: one(products, { fields: [orderItems.productId], references: [products.id] }),
}));
```
## Repository Pattern
```typescript
// lib/repositories/products.ts
import { db } from '../db';
import { products } from '../schema';
import { eq, like, and, desc, asc, sql } from 'drizzle-orm';
export const productsRepository = {
async findAll(options: { category?: string; search?: string; limit?: number } = {}) {
const { category, search, limit = 50 } = options;
const conditions = [];
if (category) conditions.push(eq(products.category, category));
if (search) conditions.push(like(products.name, `%${search}%`));
return db.select().from(products)
.where(conditions.length > 0 ? and(...conditions) : undefined)
.orderBy(desc(products.createdAt))
.limit(limit);
},
async findById(id: string) {
const [product] = await db.select().from(products).where(eq(products.id, id));
return product || null;
},
async create(data: { name: string; description?: string; price: number; category: string }) {
const [product] = await db.insert(products).values(data).returning();
return product;
},
async update(id: string, data: Partial<{ name: string; description: string; price: number; inStock: boolean }>) {
const [product] = await db.update(products).set(data).where(eq(products.id, id)).returning();
return product;
},
async delete(id: string) {
await db.delete(products).where(eq(products.id, id));
},
async getCategories() {
const result = await db.selectDistinct({ category: products.category }).from(products);
return result.map(r => r.category);
},
};
```
## Order Service
```typescript
// lib/services/orders.ts
import { db } from '../db';
import { orders, orderItems, products } from '../schema';
import { eq } from 'drizzle-orm';
interface CreateOrderInput {
userId: string;
items: Array<{ productId: string; quantity: number }>;
}
export async function createOrder(input: CreateOrderInput) {
// Calculate total
const productIds = input.items.map(i => i.productId);
const productList = await db.select().from(products).where(
sql`${products.id} IN (${productIds.map(() => '?').join(', ')})`
);
const productMap = new Map(productList.map(p => [p.id, p]));
let total = 0;
const itemsWithPrices = input.items.map(item => {
const product = productMap.get(item.productId);
if (!product) throw new Error(`Product ${item.productId} not found`);
const itemTotal = product.price * item.quantity;
total += itemTotal;
return { ...item, price: product.price };
});
// Create order and items in transaction
const [order] = await db.insert(orders).values({
userId: input.userId,
total,
status: 'pending',
}).returning();
await db.insert(orderItems).values(
itemsWithPrices.map(item => ({
orderId: order.id,
productId: item.productId,
quantity: item.quantity,
price: item.price,
}))
);
return order;
}
export async function getOrderWithItems(orderId: string) {
return db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
items: { with: { product: true } },
},
});
}
```
## Best Practices
1. **Edge Native**: SQLite at the edge with replicas
2. **Embedded Replicas**: Local read replica for low latency
3. **Sync Interval**: Configure based on consistency needs
4. **Branching**: Database branches for development
5. **Type Safety**: Full Drizzle ORM integration
6. **Global Distribution**: Multi-region replication
Google Antigravity's Gemini 3 understands Turso patterns for edge databases.This Turso 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 turso 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 Turso projects, consider mentioning your framework version, coding style, and any specific libraries you're using.