Overview
The Drizzle + Turso provider adds a typed database port to your Contract Kit application using Drizzle ORM with Tursoβs libSQL. It follows the ports & adapters pattern, providing type-safe database access while maintaining clean separation of concerns.Features
- π― Factory-based: Create providers with your schema at the call site
- π Type-safe: Full TypeScript inference from your Drizzle schema
- π Turso-ready: Works with Turso cloud or local libSQL
- ποΈ Schema-agnostic: You control where your schema files live
- π Clean separation: Runtime provider is separate from build-time CLI config
Installation
Setup
1. Define your schema
Create your Drizzle schema file wherever makes sense for your app:2. Configure Drizzle CLI (build-time)
Createdrizzle.config.ts in your app root for the Drizzle CLI:
3. Create the provider (runtime)
Import your schema and create the provider:4. Type your ports
Define your appβs ports type:5. Wire it up in your server
6. Use in your use cases
Configuration
The provider reads configuration from environment variables:| Variable | Required | Description |
|---|---|---|
TURSO_DB_URL | Yes | Turso/libSQL database URL (e.g., libsql://your-db.turso.io or file:local.db) |
TURSO_DB_AUTH_TOKEN | No | Turso auth token (required for cloud databases, optional for local) |
Example .env
API Reference
DbPort<TSchema>
The port interface exposed on ctx.ports.db:
db: The typed Drizzle database instance for ORM operationsclient: The underlying libSQL client for advanced operations not covered by Drizzle
createDrizzleTursoProvider<TSchema>(options)
Factory function to create a Drizzle Turso provider.
Parameters:
options.schema(required): Your Drizzle schema objectoptions.portName(optional): Port name, defaults to"db"
ServiceProvider that can be used with createServer
Example:
Usage Examples
Basic CRUD Operations
Transactions
Advanced libSQL Operations
Access the underlying libSQL client for operations not covered by Drizzle:Multiple Databases
You can create multiple database providers with different port names:Key Design Principles
Runtime vs. Build-time Separation
This provider follows a clean separation of concerns:-
Build-time (Drizzle CLI): Configured via
drizzle.config.ts- Used for generating migrations
- Used for introspecting the database
- Lives in your app repository
-
Runtime (Provider): Configured via factory function
- Used for connecting to the database at runtime
- Used for executing queries in your use cases
- Gets the schema from your imports
Schema Location Independence
The provider does not care where your schema file lives. You:- Define your schema file wherever makes sense (
src/db/schema.ts,db/schema.ts, etc.) - Import it in your app:
import * as schema from "@/db/schema" - Pass it to the factory:
createDrizzleTursoProvider({ schema })
Best Practices
Use migrations
Use migrations
Generate and run migrations using the Drizzle CLI to keep your database schema in sync.
Leverage TypeScript types
Leverage TypeScript types
Let Drizzleβs type system catch database schema errors at compile time.
Use transactions for multi-step operations
Use transactions for multi-step operations
Wrap multiple related database operations in transactions to maintain data consistency.
Keep schema files organized
Keep schema files organized
Organize your schema files by domain or feature for better maintainability.