turso-drizzle
TursoとDrizzle ORMを使い、型安全なSQLでエッジ環境向けのアプリケーションを構築し、SQLiteを本番環境で利用、複数リージョンデータベースやCloudflare Workersと連携させるための設定、スキーマ定義、移行、デプロイ方法を支援するSkill。
📜 元の英語説明(参考)
Turso + Drizzle ORM — type-safe SQLite at the edge with replication. Use when building edge-compatible applications with type-safe SQL, SQLite in production, multi-region databases, or Cloudflare Workers with a managed DB. Covers Turso setup, Drizzle schema definition, migrations with drizzle-kit, and edge deployment patterns.
🇯🇵 日本人クリエイター向け解説
TursoとDrizzle ORMを使い、型安全なSQLでエッジ環境向けのアプリケーションを構築し、SQLiteを本番環境で利用、複数リージョンデータベースやCloudflare Workersと連携させるための設定、スキーマ定義、移行、デプロイ方法を支援するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o turso-drizzle.zip https://jpskill.com/download/15501.zip && unzip -o turso-drizzle.zip && rm turso-drizzle.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15501.zip -OutFile "$d\turso-drizzle.zip"; Expand-Archive "$d\turso-drizzle.zip" -DestinationPath $d -Force; ri "$d\turso-drizzle.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
turso-drizzle.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
turso-drizzleフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Turso + Drizzle ORM
概要
Turso は、エッジレプリカ経由での低レイテンシ読み取りが可能な、グローバルに分散された SQLite データベースです (libSQL を利用)。Drizzle ORM は、軽量でタイプセーフな SQL クエリビルダーであり、TypeScript 向けの ORM です。これらを組み合わせることで、タイプセーフでエッジ互換の SQLite を本番環境で使用できます。
インストール
npm install drizzle-orm @libsql/client
npm install -D drizzle-kit
# or Bun
bun add drizzle-orm @libsql/client
bun add -d drizzle-kit
Turso のセットアップ
# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Authenticate
turso auth login
# Create a database
turso db create my-app
# Get the database URL
turso db show my-app --url
# libsql://my-app-yourname.turso.io
# Create an auth token
turso db tokens create my-app
# eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
.env に認証情報を保存します。
TURSO_DATABASE_URL=libsql://my-app-yourname.turso.io
TURSO_AUTH_TOKEN=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
データベースクライアント
// src/db/client.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 });
スキーマ定義
// src/db/schema.ts
import { integer, sqliteTable, text, real } from "drizzle-orm/sqlite-core";
import { relations } from "drizzle-orm";
// Users table
export const users = sqliteTable("users", {
id: integer("id").primaryKey({ autoIncrement: true }),
name: text("name").notNull(),
email: text("email").notNull().unique(),
role: text("role", { enum: ["admin", "user", "guest"] }).notNull().default("user"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Posts table
export const posts = sqliteTable("posts", {
id: integer("id").primaryKey({ autoIncrement: true }),
title: text("title").notNull(),
slug: text("slug").notNull().unique(),
body: text("body").notNull().default(""),
published: integer("published", { mode: "boolean" }).notNull().default(false),
authorId: integer("author_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Comments table
export const comments = sqliteTable("comments", {
id: integer("id").primaryKey({ autoIncrement: true }),
content: text("content").notNull(),
postId: integer("post_id")
.notNull()
.references(() => posts.id, { onDelete: "cascade" }),
authorId: integer("author_id")
.notNull()
.references(() => users.id),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Relations (for joins)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
comments: many(comments),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.authorId], references: [users.id] }),
}));
// Export inferred types
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
drizzle-kit の設定
// drizzle.config.ts
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "turso",
dbCredentials: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN,
},
} satisfies Config;
マイグレーション
# Generate migration files from schema changes
npx drizzle-kit generate
# Apply migrations to the database
npx drizzle-kit migrate
# Push schema directly (dev only — no migration files)
npx drizzle-kit push
# Open Drizzle Studio (visual DB browser)
npx drizzle-kit studio
クエリ
挿入
import { db } from "./db/client";
import { users, posts } from "./db/schema";
// Insert single row
const [user] = await db
.insert(users)
.values({ name: "Alice", email: "alice@example.com" })
.returning();
// Insert multiple rows
await db.insert(users).values([
{ name: "Bob", email: "bob@example.com" },
{ name: "Carol", email: "carol@example.com" },
]);
// Upsert (insert or update)
await db
.insert(users)
.values({ name: "Alice", email: "alice@example.com" })
.onConflictDoUpdate({
target: users.email,
set: { name: "Alice Updated" },
});
選択
import { eq, and, or, like, desc, count } from "drizzle-orm";
// Select all
const allUsers = await db.select().from(users);
// With condition
const admins = await db
.select()
.from(users)
.where(eq(users.role, "admin"));
// Multiple conditions
const activeAdmins = await db
.select({ id: users.id, name: users.name })
.from(users)
.where(and(eq(users.role, "admin"), like(users.email, "%@company.com")))
.orderBy(desc(users.createdAt))
.limit(10);
// Count
const [{ total }] = await db
.select({ total: count() })
.from(users);
結合
// Inner join
const postsWithAuthors = await db
.select({
postId: posts.id,
title: posts.title,
authorName: users.name,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt));
// With relations (using query API)
const usersWithPosts = await db.query.users.findMany({
with: {
posts: {
where: eq(posts.published, true),
orderBy: desc(posts.createdAt),
},
},
} 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Turso + Drizzle ORM
Overview
Turso is a globally distributed SQLite database (powered by libSQL) with low-latency reads via edge replicas. Drizzle ORM is a lightweight, type-safe SQL query builder and ORM for TypeScript. Together they enable type-safe, edge-compatible SQLite in production.
Installation
npm install drizzle-orm @libsql/client
npm install -D drizzle-kit
# or Bun
bun add drizzle-orm @libsql/client
bun add -d drizzle-kit
Turso Setup
# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Authenticate
turso auth login
# Create a database
turso db create my-app
# Get the database URL
turso db show my-app --url
# libsql://my-app-yourname.turso.io
# Create an auth token
turso db tokens create my-app
# eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
Store credentials in .env:
TURSO_DATABASE_URL=libsql://my-app-yourname.turso.io
TURSO_AUTH_TOKEN=eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
Database Client
// src/db/client.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 });
Schema Definition
// src/db/schema.ts
import { integer, sqliteTable, text, real } from "drizzle-orm/sqlite-core";
import { relations } from "drizzle-orm";
// Users table
export const users = sqliteTable("users", {
id: integer("id").primaryKey({ autoIncrement: true }),
name: text("name").notNull(),
email: text("email").notNull().unique(),
role: text("role", { enum: ["admin", "user", "guest"] }).notNull().default("user"),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Posts table
export const posts = sqliteTable("posts", {
id: integer("id").primaryKey({ autoIncrement: true }),
title: text("title").notNull(),
slug: text("slug").notNull().unique(),
body: text("body").notNull().default(""),
published: integer("published", { mode: "boolean" }).notNull().default(false),
authorId: integer("author_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Comments table
export const comments = sqliteTable("comments", {
id: integer("id").primaryKey({ autoIncrement: true }),
content: text("content").notNull(),
postId: integer("post_id")
.notNull()
.references(() => posts.id, { onDelete: "cascade" }),
authorId: integer("author_id")
.notNull()
.references(() => users.id),
createdAt: integer("created_at", { mode: "timestamp" })
.notNull()
.$defaultFn(() => new Date()),
});
// Relations (for joins)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
comments: many(comments),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.authorId], references: [users.id] }),
}));
// Export inferred types
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
drizzle-kit Configuration
// drizzle.config.ts
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "turso",
dbCredentials: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN,
},
} satisfies Config;
Migrations
# Generate migration files from schema changes
npx drizzle-kit generate
# Apply migrations to the database
npx drizzle-kit migrate
# Push schema directly (dev only — no migration files)
npx drizzle-kit push
# Open Drizzle Studio (visual DB browser)
npx drizzle-kit studio
Queries
Insert
import { db } from "./db/client";
import { users, posts } from "./db/schema";
// Insert single row
const [user] = await db
.insert(users)
.values({ name: "Alice", email: "alice@example.com" })
.returning();
// Insert multiple rows
await db.insert(users).values([
{ name: "Bob", email: "bob@example.com" },
{ name: "Carol", email: "carol@example.com" },
]);
// Upsert (insert or update)
await db
.insert(users)
.values({ name: "Alice", email: "alice@example.com" })
.onConflictDoUpdate({
target: users.email,
set: { name: "Alice Updated" },
});
Select
import { eq, and, or, like, desc, count } from "drizzle-orm";
// Select all
const allUsers = await db.select().from(users);
// With condition
const admins = await db
.select()
.from(users)
.where(eq(users.role, "admin"));
// Multiple conditions
const activeAdmins = await db
.select({ id: users.id, name: users.name })
.from(users)
.where(and(eq(users.role, "admin"), like(users.email, "%@company.com")))
.orderBy(desc(users.createdAt))
.limit(10);
// Count
const [{ total }] = await db
.select({ total: count() })
.from(users);
Join
// Inner join
const postsWithAuthors = await db
.select({
postId: posts.id,
title: posts.title,
authorName: users.name,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt));
// With relations (using query API)
const usersWithPosts = await db.query.users.findMany({
with: {
posts: {
where: eq(posts.published, true),
orderBy: desc(posts.createdAt),
},
},
});
Update and Delete
import { eq } from "drizzle-orm";
// Update
const [updated] = await db
.update(users)
.set({ role: "admin" })
.where(eq(users.id, 1))
.returning();
// Delete
await db.delete(posts).where(eq(posts.authorId, 1));
Cloudflare Workers Pattern
In Cloudflare Workers, create the client per request (no persistent connections):
// src/index.ts
import { Hono } from "hono";
import { createClient } from "@libsql/client/http"; // Use HTTP client for edge
import { drizzle } from "drizzle-orm/libsql";
import * as schema from "./db/schema";
import { eq } from "drizzle-orm";
type Env = {
TURSO_DATABASE_URL: string;
TURSO_AUTH_TOKEN: string;
};
const app = new Hono<{ Bindings: Env }>();
// Middleware: attach db to context
app.use("*", async (c, next) => {
const client = createClient({
url: c.env.TURSO_DATABASE_URL,
authToken: c.env.TURSO_AUTH_TOKEN,
});
c.set("db", drizzle(client, { schema }));
await next();
});
app.get("/users", async (c) => {
const db = c.get("db");
const allUsers = await db.select().from(schema.users);
return c.json({ users: allUsers });
});
app.get("/users/:id", async (c) => {
const db = c.get("db");
const id = Number(c.req.param("id"));
const [user] = await db
.select()
.from(schema.users)
.where(eq(schema.users.id, id));
if (!user) return c.json({ error: "Not found" }, 404);
return c.json({ user });
});
export default app;
# wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-11-01"
compatibility_flags = ["nodejs_compat"]
[vars]
TURSO_DATABASE_URL = "libsql://my-app-yourname.turso.io"
# Set TURSO_AUTH_TOKEN via: wrangler secret put TURSO_AUTH_TOKEN
Embedded Replica Pattern
For maximum read performance with Turso:
// src/db/client.ts — Node.js / Bun only (not edge)
import { createClient } from "@libsql/client";
import { drizzle } from "drizzle-orm/libsql";
import * as schema from "./schema";
const client = createClient({
url: "file:./local-replica.db",
syncUrl: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
syncInterval: 60, // Sync every 60s
});
// Initial sync on startup
await client.sync();
export const db = drizzle(client, { schema });
Guidelines
- Use
drizzle-orm/libsqladapter for both local SQLite and Turso cloud. - Use
@libsql/client/http(not the default) on Cloudflare Workers — native bindings are not supported. - Always use
.returning()after insert/update to get the created/updated row. - Define
relations()for clean join queries with the Drizzle query API. - Run
drizzle-kit generatethendrizzle-kit migratefor production migrations — neverpushin production. - Store
TURSO_AUTH_TOKENas a secret (wrangler secret put) — never inwrangler.toml. - Use embedded replicas for Node.js/Bun apps that need fast reads with global replication.
- Export
$inferSelectand$inferInserttypes from the schema for use in application code.