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/tursoon 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
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.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.