MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr 🔥 tagline

Hey there 👋 I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 — present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 — Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms — earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

Edge Databases: Turso, Neon, and PlanetScale

Compare edge databases: replication, latency, serverless drivers, and pricing.

EdgeDatabaseTursoNeon

By MinhVo

Introduction

The database layer has traditionally been the bottleneck in global application architectures. While compute and caching have moved to the edge, databases remained anchored in a single region, forcing every query to travel thousands of kilometers. Edge databases change this by replicating data to locations worldwide, enabling queries with single-digit millisecond latency from anywhere on the planet.

The edge database landscape has matured rapidly. Turso, built on SQLite's libSQL fork, replicates lightweight databases to 35+ locations. Neon pioneered serverless PostgreSQL with scale-to-zero and branching. PlanetScale (built on Vitess) offers MySQL-compatible sharding with online schema migrations. Each represents a different philosophy on how to bring data closer to users.

This guide compares these three edge databases across architecture, latency, developer experience, pricing, and production readiness. You'll learn when to choose each one, how to integrate them with edge functions, and the patterns that make edge data access fast and reliable.

Database architecture visualization showing global replication

Understanding Edge Databases: Core Concepts

What Makes a Database "Edge-Native"

An edge database is not simply a traditional database deployed in multiple regions. Edge-native databases are designed from the ground up for the constraints of edge computing: sub-10ms query latency, serverless scaling, minimal connection overhead, and compatibility with edge runtimes that lack full Node.js support.

The key architectural difference is replication strategy. Traditional databases replicate for high availability — a standby server takes over if the primary fails. Edge databases replicate for latency — data is copied to every location where users exist, so queries hit a local replica instead of traveling to a primary region.

This creates a fundamental consistency trade-off. Strong consistency requires every write to be acknowledged by all replicas before returning, which defeats the latency advantage. Most edge databases offer eventual consistency for reads with strong consistency for writes, accepting that reads might be a few milliseconds behind the primary.

The Serverless Connection Model

Traditional databases use persistent TCP connections — a connection pool sits in front of the database and maintains long-lived connections to the server. This model doesn't work at the edge because edge functions are ephemeral — they spin up, handle a request, and disappear. Maintaining a persistent connection per edge function would overwhelm the database.

Edge databases solve this with HTTP-based or WebSocket-based protocols. Instead of TCP connections, each query is an HTTP request. This eliminates connection pooling overhead and works perfectly with edge runtimes. The trade-off is slightly higher per-query latency (1-2ms for HTTP overhead) but this is negligible compared to the latency saved by edge replication.

// Traditional database: persistent TCP connection
import { Pool } from 'pg';
const pool = new Pool({ connectionString: 'postgresql://...' });
const client = await pool.connect();
const result = await client.query('SELECT * FROM users');
client.release();
 
// Edge database: HTTP-based queries (no connection pool)
import { createClient } from '@libsql/client';
const db = createClient({ url: 'libsql://your-db.turso.io', authToken: '...' });
const result = await db.execute('SELECT * FROM users');
// No connection management needed

Replication Topologies

Edge databases use different replication topologies:

Single-Primary, Multi-Replica: One primary handles all writes, replicas serve reads globally. This is the simplest model but writes are limited by the primary's location. Turso uses this model.

Multi-Primary: Multiple locations can accept writes, with conflict resolution. More complex but lower write latency. CockroachDB and YugabyteDB use this model.

Branching: Neon's unique approach creates database branches (like git branches) for development and preview environments. Each branch is a full copy that can diverge from the main branch.

Global database replication topology diagram

Architecture and Design Patterns

Turso Architecture

Turso is built on libSQL, an open-source fork of SQLite maintained by ChiselStrike. The core insight is that SQLite's single-file database format is perfect for edge replication — a database small enough to fit in a single file is small enough to replicate to 35+ locations.

// Turso client with automatic nearest-replica routing
import { createClient } from '@libsql/client';
 
const db = createClient({
  url: 'libsql://your-db.turso.io',
  authToken: process.env.TURSO_AUTH_TOKEN,
});
 
// This query runs against the nearest edge replica
const users = await db.execute('SELECT * FROM users WHERE active = 1');
 
// Batch queries for efficiency
const [users, posts, comments] = await db.batch([
  'SELECT * FROM users LIMIT 10',
  'SELECT * FROM posts ORDER BY created_at DESC LIMIT 20',
  'SELECT COUNT(*) as count FROM comments',
]);

Turso's key advantage is simplicity. SQLite is the most widely deployed database in the world, and libSQL maintains full compatibility. Existing SQLite tools, ORMs, and knowledge transfer directly. The trade-off is that Turso databases are limited in size (typically under 10GB) and don't support the full SQL feature set of PostgreSQL.

Neon Architecture

Neon is serverless PostgreSQL. It separates storage and compute — the storage layer is a custom distributed system that replicates data across multiple availability zones, while the compute layer runs PostgreSQL instances that scale to zero when idle.

Neon's killer feature is branching. You can create a database branch (like a git branch) that is a full copy of your database at a point in time. This is transformative for development workflows — each pull request gets its own database branch with real data, and branches can be created in milliseconds regardless of database size.

// Neon serverless driver for edge compatibility
import { neon } from '@neondatabase/serverless';
 
const sql = neon(process.env.DATABASE_URL!);
 
// Simple query — HTTP-based, works at the edge
const users = await sql('SELECT * FROM users WHERE active = $1', [true]);
 
// Transaction support
const result = await sql.transaction([
  sql`INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id`,
  sql`UPDATE users SET order_count = order_count + 1 WHERE id = $1`,
]);

Neon's edge support comes through its serverless driver, which uses HTTP for queries instead of TCP connections. This makes it compatible with Cloudflare Workers, Vercel Edge Functions, and Deno Deploy. The trade-off is that each query has HTTP overhead (~1-2ms) and the driver doesn't support cursors or streaming results.

PlanetScale Architecture

PlanetScale is built on Vitess, the database sharding system originally developed at YouTube. It provides MySQL-compatible horizontal sharding with online schema migrations — you can alter tables without downtime or locking.

PlanetScale's unique feature is deploy requests. Schema changes are proposed as deploy requests (like pull requests), reviewed, and applied with zero downtime. Vitess handles the online schema migration internally, using a shadow table approach that copies data to the new schema in the background while the old schema continues serving traffic.

// PlanetScale serverless driver
import { connect } from '@planetscale/database';
 
const conn = connect({
  url: process.env.DATABASE_URL,
});
 
// Query with connection pooling handled by PlanetScale
const users = await conn.execute('SELECT * FROM users WHERE active = ?', [true]);
 
// Prepared statements for security
const user = await conn.execute(
  'SELECT * FROM users WHERE id = ?',
  [userId]
);

PlanetScale's edge support comes through its serverless driver, similar to Neon. However, PlanetScale is MySQL-compatible, which may be a better fit for teams with MySQL experience or existing MySQL schemas.

Step-by-Step Implementation

Let's build a complete edge application that demonstrates all three databases with Cloudflare Workers and Hono.

Setting Up the Edge Database Application

npm create cloudflare@latest edge-db-demo -- --type=hello-world
cd edge-db-demo
npm install hono @libsql/client @neondatabase/serverless @planetscale/database

Turso Integration

import { Hono } from 'hono';
import { createClient } from '@libsql/client';
 
const app = new Hono();
 
app.get('/api/turso/users', async (c) => {
  const db = createClient({
    url: c.env.TURSO_URL,
    authToken: c.env.TURSO_AUTH_TOKEN,
  });
 
  const start = performance.now();
  const result = await db.execute('SELECT * FROM users LIMIT 20');
  const latency = performance.now() - start;
 
  return c.json({
    source: 'turso',
    latency: `${latency.toFixed(2)}ms`,
    count: result.rows.length,
    users: result.rows,
  });
});

Neon Integration

import { neon } from '@neondatabase/serverless';
 
app.get('/api/neon/users', async (c) => {
  const sql = neon(c.env.NEON_DATABASE_URL);
 
  const start = performance.now();
  const users = await sql('SELECT * FROM users LIMIT 20');
  const latency = performance.now() - start;
 
  return c.json({
    source: 'neon',
    latency: `${latency.toFixed(2)}ms`,
    count: users.length,
    users,
  });
});

PlanetScale Integration

import { connect } from '@planetscale/database';
 
app.get('/api/planetscale/users', async (c) => {
  const conn = connect({ url: c.env.PLANETSCALE_URL });
 
  const start = performance.now();
  const result = await conn.execute('SELECT * FROM users LIMIT 20');
  const latency = performance.now() - start;
 
  return c.json({
    source: 'planetscale',
    latency: `${latency.toFixed(2)}ms`,
    count: result.rows.length,
    users: result.rows,
  });
});

Database Benchmark Comparison

app.get('/api/benchmark', async (c) => {
  const results: Record<string, number> = {};
 
  // Turso benchmark
  const tursoDb = createClient({
    url: c.env.TURSO_URL,
    authToken: c.env.TURSO_AUTH_TOKEN,
  });
  const tursoStart = performance.now();
  await tursoDb.execute('SELECT * FROM users LIMIT 100');
  results.turso = performance.now() - tursoStart;
 
  // Neon benchmark
  const neonSql = neon(c.env.NEON_DATABASE_URL);
  const neonStart = performance.now();
  await neonSql('SELECT * FROM users LIMIT 100');
  results.neon = performance.now() - neonStart;
 
  // PlanetScale benchmark
  const psConn = connect({ url: c.env.PLANETSCALE_URL });
  const psStart = performance.now();
  await psConn.execute('SELECT * FROM users LIMIT 100');
  results.planetscale = performance.now() - psStart;
 
  return c.json(results);
});

Edge database performance comparison chart

Real-World Use Cases and Case Studies

Use Case 1: Global SaaS with Turso

A multi-tenant SaaS platform serves 5,000 customers across 30 countries. Using Turso, each tenant's data is replicated to the nearest edge location. Read queries complete in 2-5ms regardless of user location. The platform's P99 latency dropped from 340ms (single-region PostgreSQL) to 15ms (Turso edge replicas). The SQLite compatibility meant the existing team could migrate without learning a new query language.

Use Case 2: Development Workflow with Neon

A startup uses Neon's branching feature to create database branches for every pull request. When a developer opens a PR, CI creates a Neon branch with a copy of production data (anonymized), runs integration tests against it, and destroys the branch when the PR merges. This eliminates the "works on my machine" database problem and reduced database-related production incidents by 60%.

Use Case 3: High-Scale E-Commerce with PlanetScale

An e-commerce platform processing 50,000 orders per hour uses PlanetScale for its horizontal sharding capability. The database is sharded by tenant ID, with each shard handling up to 10,000 orders per minute. Deploy requests handle schema migrations for new features without downtime — the platform has deployed 200+ schema changes in the past year without a single minute of downtime.

Best Practices for Production

  1. Choose based on data model complexity: Turso excels for simple schemas (< 100 tables) with moderate data volumes. Neon is best for complex PostgreSQL schemas with joins, CTEs, and advanced SQL. PlanetScale is ideal for high-write workloads that need horizontal sharding.

  2. Implement read-write splitting: Route reads to edge replicas and writes to the primary. Most edge databases support this automatically, but verify the routing behavior for your specific use case.

  3. Use connection pooling for traditional drivers: If you must use a traditional database driver (not the HTTP-based edge driver), use connection pooling. PgBouncer for PostgreSQL, ProxySQL for MySQL.

  4. Monitor replication lag: Edge replicas may lag behind the primary by milliseconds to seconds. Monitor this lag and alert if it exceeds acceptable thresholds for your application.

  5. Implement retry logic with exponential backoff: Edge database connections can fail due to network issues between edge locations. Implement retry logic with exponential backoff and circuit breaker patterns.

  6. Use prepared statements: All three databases support prepared statements, which prevent SQL injection and improve query performance through plan caching.

  7. Cache at the edge layer: Don't query the edge database for data that changes infrequently. Use edge KV or CDN caching for reference data, and only query the database for dynamic user-specific data.

  8. Plan for data migration: If migrating from a traditional database, plan the data migration carefully. Turso requires SQLite format, Neon accepts PostgreSQL dumps, and PlanetScale accepts MySQL imports. Use schema conversion tools for cross-database migrations.

Common Pitfalls and Solutions

PitfallImpactSolution
Querying from edge to single-region DBNo latency improvementUse edge databases with replicated reads
Assuming strong consistency on readsStale data after writesRead from primary for consistency-critical queries
Large database on TursoPerformance degradation, size limitsKeep Turso databases under 10GB, shard for larger datasets
Missing connection poolingConnection exhaustion under loadUse HTTP-based edge drivers or connection poolers
Ignoring replication lagServing outdated dataMonitor lag, use version vectors, read-after-write consistency
No failover strategyComplete outage on replica failureImplement multi-region fallback, cache stale data

Performance Optimization

// Edge database performance monitoring
function withLatencyTracking<T>(
  name: string,
  fn: () => Promise<T>
): Promise<{ result: T; latency: number }> {
  return (async () => {
    const start = performance.now();
    const result = await fn();
    const latency = performance.now() - start;
 
    // Log slow queries
    if (latency > 50) {
      console.warn(`Slow query [${name}]: ${latency.toFixed(2)}ms`);
    }
 
    return { result, latency };
  })();
}
 
// Usage with edge database
const { result: users, latency } = await withLatencyTracking('users-list', () =>
  db.execute('SELECT * FROM users WHERE active = 1')
);
 
console.log(`Query took ${latency.toFixed(2)}ms, returned ${users.rows.length} rows`);

Comparison with Alternatives

FeatureTursoNeonPlanetScaleTraditional PostgreSQL
Database EngineSQLite (libSQL)PostgreSQLMySQL (Vitess)PostgreSQL
Edge Replicas35+ locations3 regionsMulti-regionNone
Read Latency (edge)2-5ms5-15ms5-15ms50-300ms
Write Latency20-50ms20-50ms10-30ms1-5ms
Max Database Size~10GBUnlimitedUnlimitedUnlimited
BranchingNoYes (git-like)NoNo
Schema MigrationsManualManualDeploy requestsManual
Scale to ZeroYesYesNoNo
Pricing ModelPer-queryPer-compute hourPer-row readPer-instance
Edge DriverHTTP (native)HTTP (serverless)HTTP (serverless)TCP (not edge-compatible)

Advanced Patterns

Multi-Database Architecture

Use different databases for different concerns:

// Turso for read-heavy data (products, content)
const turso = createClient({ url: env.TURSO_URL, authToken: env.TURSO_TOKEN });
 
// Neon for complex relational data (orders, analytics)
const neon = neon(env.NEON_URL);
 
// KV for caching and sessions
const kv = env.KV;
 
app.get('/api/products/:id', async (c) => {
  const id = c.req.param('id');
 
  // L1: Edge KV cache
  const cached = await kv.get(`product:${id}`, 'json');
  if (cached) return c.json(cached);
 
  // L2: Turso edge replica (fast reads)
  const product = await turso.execute({
    sql: 'SELECT * FROM products WHERE id = ?',
    args: [id],
  });
 
  if (product.rows.length === 0) return c.json({ error: 'Not found' }, 404);
 
  // Cache for 5 minutes
  await kv.put(`product:${id}`, JSON.stringify(product.rows[0]), {
    expirationTtl: 300,
  });
 
  return c.json(product.rows[0]);
});

Edge Database with Local Cache

// LRU cache at the edge for ultra-fast repeated reads
const cache = new Map<string, { data: unknown; expiry: number }>();
 
async function cachedQuery<T>(
  db: LibsqlClient,
  key: string,
  sql: string,
  args: unknown[],
  ttlMs: number = 5000
): Promise<T> {
  const cached = cache.get(key);
  if (cached && cached.expiry > Date.now()) {
    return cached.data as T;
  }
 
  const result = await db.execute({ sql, args });
  cache.set(key, { data: result.rows, expiry: Date.now() + ttlMs });
 
  return result.rows as T;
}

Testing Strategies

import { unstable_dev } from 'wrangler';
 
describe('Edge Database Integration', () => {
  let worker: any;
 
  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true },
    });
  });
 
  afterAll(async () => {
    await worker.stop();
  });
 
  test('Turso query completes within 20ms', async () => {
    const resp = await worker.fetch('/api/turso/users');
    const data = await resp.json();
    expect(data.latency).toBeDefined();
    expect(parseFloat(data.latency)).toBeLessThan(20);
  });
 
  test('Neon query returns correct data', async () => {
    const resp = await worker.fetch('/api/neon/users');
    const data = await resp.json();
    expect(data.users).toBeDefined();
    expect(Array.isArray(data.users)).toBe(true);
  });
 
  test('benchmark compares all databases', async () => {
    const resp = await worker.fetch('/api/benchmark');
    const data = await resp.json();
    expect(data).toHaveProperty('turso');
    expect(data).toHaveProperty('neon');
    expect(data).toHaveProperty('planetscale');
  });
});

Future Outlook

Edge databases are converging toward a unified model: serverless scaling, edge replication, HTTP-based protocols, and compatibility with all major edge runtimes. Turso is expanding its SQL feature set. Neon is adding more edge locations. PlanetScale is improving its edge driver.

The next frontier is edge-native features: vector search for AI applications, full-text search for content platforms, and real-time subscriptions for collaborative applications. These features are already available in traditional databases but are being adapted for the edge's unique constraints.

Conclusion

Edge databases are the missing piece in the edge computing puzzle. Without edge data, edge functions are limited to caching and routing. With Turso, Neon, or PlanetScale, edge functions can query data with single-digit millisecond latency from anywhere in the world.

Key takeaways:

  1. Turso is ideal for simple schemas and SQLite-compatible workloads with global reads
  2. Neon excels for complex PostgreSQL schemas with branching workflows
  3. PlanetScale is best for high-write workloads needing horizontal sharding
  4. All three offer HTTP-based edge drivers compatible with edge runtimes
  5. Implement multi-layer caching: edge KV → edge database → origin
  6. Monitor replication lag and implement retry logic for edge database access

Choose based on your team's existing expertise (SQLite → Turso, PostgreSQL → Neon, MySQL → PlanetScale), your data model complexity, and your scaling requirements. The edge database landscape is evolving rapidly, and all three are production-ready for the right use case.