/ astro-integrations / How to Use Turso with Astro: Complete Guide
astro-integrations 4 min read

How to Use Turso with Astro: Complete Guide

Step-by-step guide to integrating Turso with your Astro website.

Turso is an edge-native database built on libSQL, which is an open-source fork of SQLite. Instead of running a centralized database server, Turso replicates your data to edge locations close to your users. The result is read latencies measured in single-digit milliseconds, no matter where your visitors are.

For Astro projects that need a database but want to keep things lightweight, Turso is a compelling option. You get the simplicity of SQLite with the scalability of a distributed database. No connection pooling headaches, no complex cluster management.

Prerequisites

  • Node.js 18+
  • An Astro project (npm create astro@latest)
  • A Turso account (free tier gives you 500 databases and 9GB storage)
  • The Turso CLI installed (brew install tursodatabase/tap/turso on macOS)

Installation

Install the Turso client library:

npm install @libsql/client

Create a database using the Turso CLI:

turso db create my-astro-blog
turso db show my-astro-blog --url
turso db tokens create my-astro-blog

Save the URL and token. You will need them in the next step.

Configuration

Add your Turso credentials to .env:

TURSO_DATABASE_URL=libsql://your-database-name-your-org.turso.io
TURSO_AUTH_TOKEN=your_auth_token_here

Create a database client helper:

// src/lib/turso.ts
import { createClient } from "@libsql/client";

export const turso = createClient({
  url: import.meta.env.TURSO_DATABASE_URL,
  authToken: import.meta.env.TURSO_AUTH_TOKEN,
});

Set up your schema. You can run migrations through the Turso CLI or programmatically:

turso db shell my-astro-blog
CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  slug TEXT NOT NULL UNIQUE,
  content TEXT NOT NULL,
  published_at TEXT DEFAULT (datetime('now')),
  draft INTEGER DEFAULT 0
);

CREATE TABLE comments (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  post_id INTEGER REFERENCES posts(id),
  author TEXT NOT NULL,
  body TEXT NOT NULL,
  created_at TEXT DEFAULT (datetime('now'))
);

Basic Usage

Fetch posts from Turso in your Astro pages:

---
// src/pages/blog/index.astro
import { turso } from "../../lib/turso";
import BaseLayout from "../../layouts/BaseLayout.astro";

const result = await turso.execute(
  "SELECT id, title, slug, published_at FROM posts WHERE draft = 0 ORDER BY published_at DESC"
);

const posts = result.rows;
---

<BaseLayout title="Blog">
  <h1>Blog</h1>
  <ul>
    {posts.map((post) => (
      <li>
        <a href={`/blog/${post.slug}`}>
          <h2>{post.title}</h2>
          <time>{post.published_at}</time>
        </a>
      </li>
    ))}
  </ul>
</BaseLayout>

For individual post pages with SSR:

---
// src/pages/blog/[slug].astro
import { turso } from "../../lib/turso";
import BaseLayout from "../../layouts/BaseLayout.astro";

const { slug } = Astro.params;

const postResult = await turso.execute({
  sql: "SELECT * FROM posts WHERE slug = ? AND draft = 0",
  args: [slug],
});

if (postResult.rows.length === 0) {
  return Astro.redirect("/404");
}

const post = postResult.rows[0];

const commentsResult = await turso.execute({
  sql: "SELECT * FROM comments WHERE post_id = ? ORDER BY created_at DESC",
  args: [post.id],
});
---

<BaseLayout title={post.title}>
  <article>
    <h1>{post.title}</h1>
    <div set:html={post.content} />
  </article>
  <section>
    <h2>Comments</h2>
    {commentsResult.rows.map((comment) => (
      <div>
        <strong>{comment.author}</strong>
        <p>{comment.body}</p>
      </div>
    ))}
  </section>
</BaseLayout>

Production Tips

  1. Use embedded replicas for static builds. Turso supports embedded replicas that sync to a local SQLite file. During astro build, queries hit the local file instead of the network, making builds faster and more reliable.

  • Use parameterized queries. Always use args for user input instead of string interpolation. This prevents SQL injection and is the correct way to use the libSQL client.

  • Batch related queries. Turso supports batching multiple statements in a single request. Use turso.batch() when you need to run several queries that do not depend on each other.

  • Set up database groups for multi-region. If your audience is global, create a database group with replicas in multiple regions. Turso routes queries to the nearest replica automatically.

  • Monitor your usage. The free tier includes 9GB of total storage and 500 databases. For read-heavy sites, Turso performs well within the free tier. Write-heavy applications should monitor row counts and storage.

  • Alternatives to Consider

    • Supabase if you want a full PostgreSQL database with authentication, storage, and real-time subscriptions included.
    • PlanetScale if you prefer MySQL and need branching for schema changes in a team environment.
    • Neon if you want serverless PostgreSQL with a generous free tier and automatic scaling.

    Wrapping Up

    Turso brings the simplicity of SQLite to the edge. For Astro projects that need a database, whether for comments, user data, or dynamic content, Turso provides a lightweight solution with excellent performance. The free tier is generous, the client library is straightforward, and the edge replication means your data is always close to your users.