DEV Community

Alex Spinov
Alex Spinov

Posted on

Turso Has a Free API — Here's How to Build Edge-First Apps with Embedded SQLite

Why Turso?

Turso brings SQLite to the edge. It's libSQL (a fork of SQLite) hosted globally with replication to 30+ locations. Your database is literally milliseconds from your users — everywhere.

Free tier: 9 GB storage, 500 databases, 25M row reads/month, 5 locations.

Getting Started

Install CLI

# macOS
brew install tursodatabase/tap/turso

# Linux
curl -sSfL https://get.tur.so/install.sh | bash

turso auth login
Enter fullscreen mode Exit fullscreen mode

Create Database

turso db create my-app
turso db show my-app --url
# libsql://my-app-yourname.turso.io

turso db tokens create my-app
# Your auth token
Enter fullscreen mode Exit fullscreen mode

TypeScript (with Drizzle ORM)

import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { eq, desc, sql } from "drizzle-orm";

const client = createClient({
  url: "libsql://my-app-yourname.turso.io",
  authToken: "YOUR_TOKEN"
});

const db = drizzle(client);

// Define schema
const users = sqliteTable("users", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  name: text("name").notNull(),
  email: text("email").unique().notNull(),
  plan: text("plan").default("free")
});

const posts = sqliteTable("posts", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  title: text("title").notNull(),
  content: text("content"),
  authorId: integer("author_id").references(() => users.id),
  views: integer("views").default(0)
});

// Query
const topPosts = await db.select({
  title: posts.title,
  views: posts.views,
  author: users.name
}).from(posts)
  .innerJoin(users, eq(posts.authorId, users.id))
  .orderBy(desc(posts.views))
  .limit(10);

topPosts.forEach(p => console.log(`${p.title} by ${p.author} (${p.views} views)`));
Enter fullscreen mode Exit fullscreen mode

Python

import libsql_experimental as libsql

conn = libsql.connect("my-app",
    sync_url="libsql://my-app-yourname.turso.io",
    auth_token="YOUR_TOKEN")
conn.sync()

# Create table
conn.execute("""
    CREATE TABLE IF NOT EXISTS products (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        price REAL NOT NULL,
        category TEXT,
        in_stock INTEGER DEFAULT 1
    )
""")

# Insert
conn.executemany(
    "INSERT INTO products (name, price, category) VALUES (?, ?, ?)",
    [("Widget", 9.99, "tools"), ("Gadget", 24.99, "electronics"), ("Doohickey", 14.99, "tools")]
)
conn.commit()
conn.sync()  # Sync to cloud

# Query
results = conn.execute("""
    SELECT category, COUNT(*) as count, AVG(price) as avg_price
    FROM products WHERE in_stock = 1
    GROUP BY category ORDER BY avg_price DESC
""").fetchall()

for row in results:
    print(f"{row[0]}: {row[1]} products, avg ${row[2]:.2f}")
Enter fullscreen mode Exit fullscreen mode

Embedded Replicas (Killer Feature)

import { createClient } from "@libsql/client";

// Local SQLite file synced with Turso cloud!
const client = createClient({
  url: "file:local-replica.db",
  syncUrl: "libsql://my-app-yourname.turso.io",
  authToken: "YOUR_TOKEN",
  syncInterval: 60 // sync every 60 seconds
});

// Reads are LOCAL (0ms latency!)
const result = await client.execute("SELECT * FROM products WHERE category = 'tools'");

// Writes go to cloud and sync back
await client.execute({ sql: "INSERT INTO products (name, price) VALUES (?, ?)", args: ["Thingamajig", 19.99] });
Enter fullscreen mode Exit fullscreen mode

Turso vs Alternatives

Feature Turso Supabase PlanetScale D1
Engine SQLite PostgreSQL MySQL SQLite
Edge replicas Yes No No Yes
Embedded mode Yes No No No
Free storage 9 GB 500 MB 5 GB 5 GB
Locations 30+ 1 3+ 300+

Need data for your Turso-powered app? I build production-ready scrapers. Check out my Apify actors or email spinov001@gmail.com for custom data pipelines.

Using Turso? How are embedded replicas working for you? Share below!

Top comments (0)