This example demonstrates how to use refine-sqlx with Cloudflare D1 in a Workers environment.
All D1 features are available in both packages with 100% identical APIs:
| Feature | Main Package | D1 Package | Bundle Size |
|---|---|---|---|
createRefineSQL() |
refine-sqlx |
refine-sqlx/d1 |
10 KB vs 67 KB |
batchInsert() |
✅ | ✅ | Same API |
batchUpdate() |
✅ | ✅ | Same API |
batchDelete() |
✅ | ✅ | Same API |
D1Options |
✅ | ✅ | Same types |
You can use either:
// Option 1: Main package (smallest bundle, all DB support)
import { batchInsert, createRefineSQL } from 'refine-sqlx';
// Option 2: D1-optimized package (self-contained, includes Drizzle ORM)
import { batchInsert, createRefineSQL } from 'refine-sqlx/d1';
// ✅ Code is identical - only import path differs!- ✅ Optimized D1 build (
refine-sqlx/d1) - ✅ Type-safe schema with Drizzle ORM
- ✅ Complete REST API for users and posts
- ✅ Automatic pagination and filtering
- ✅ Error handling
- ✅ Batch operations for bulk inserts/updates
- ✅ Time Travel configuration support
npm install refine-sqlx drizzle-orm
npm install -D wrangler @cloudflare/workers-typesname = "refine-sqlx-d1-example"
main = "example/d1-worker.ts"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB"
database_name = "refine-sqlx-demo"
database_id = "your-database-id"# Create database
wrangler d1 create refine-sqlx-demo
# Copy the database_id to wrangler.toml# Set environment variables
export CLOUDFLARE_ACCOUNT_ID="your-account-id"
export CLOUDFLARE_DATABASE_ID="your-database-id"
export CLOUDFLARE_API_TOKEN="your-api-token"
# Generate migrations from schema
bun run db:generate:d1
# Apply migrations to D1
bun run db:migrate:d1
# Or apply manually
wrangler d1 execute refine-sqlx-demo --file=./drizzle/d1/0000_initial.sql# Start local development
wrangler dev
# Initialize tables (POST to /init)
curl -X POST http://localhost:8787/initGET /users?page=1&pageSize=10&status=active- List users with pagination and filteringGET /users/:id- Get single userPOST /users- Create user{ "name": "John Doe", "email": "john@example.com", "status": "active" }PUT /users/:id- Update userDELETE /users/:id- Delete user
GET /posts?page=1&pageSize=10- List postsPOST /posts- Create post{ "title": "My Post", "content": "Post content...", "userId": 1, "status": "published" }
POST /init- Initialize database schema and sample data
# Build
npm run build
# Deploy to Cloudflare
wrangler deploy⚡ Available in both packages: refine-sqlx and refine-sqlx/d1
The batch operations automatically handle D1's 50-statement batch limit:
// Works with both 'refine-sqlx' and 'refine-sqlx/d1'
import {
batchDelete,
batchInsert,
batchUpdate,
createRefineSQL,
} from 'refine-sqlx/d1';
// Create data provider
const dataProvider = await createRefineSQL({
connection: env.DB,
schema,
d1Options: {
batch: { maxSize: 50 }, // Optional: customize batch size
},
});
// Batch insert - automatically chunks into optimal batches
const users = await batchInsert(dataProvider, 'users', [
{
name: 'User 1',
email: 'user1@example.com',
status: 'active',
createdAt: new Date(),
},
{
name: 'User 2',
email: 'user2@example.com',
status: 'active',
createdAt: new Date(),
},
// ... up to thousands of items
]);
// Batch update
const updated = await batchUpdate(dataProvider, 'users', [1, 2, 3, 4], {
status: 'inactive',
});
// Batch delete
const deleted = await batchDelete(dataProvider, 'users', [10, 11, 12]);⚡ Available in both packages: refine-sqlx and refine-sqlx/d1
D1's Time Travel feature allows you to restore databases to specific points in time. While runtime queries at specific timestamps are not supported by D1's API, you can configure Time Travel awareness:
// Works with both 'refine-sqlx' and 'refine-sqlx/d1'
const dataProvider = await createRefineSQL({
connection: env.DB,
schema,
d1Options: {
timeTravel: {
enabled: true,
bookmark: 'before-migration', // or Unix timestamp
},
},
});Note: For actual database restoration, use the Wrangler CLI:
# Restore to specific timestamp
wrangler d1 time-travel restore refine-sqlx-demo --timestamp=1234567890
# Restore to bookmark
wrangler d1 time-travel restore refine-sqlx-demo --bookmark=before-migration
# Create bookmark for current state
wrangler d1 time-travel bookmark create refine-sqlx-demo --name=before-migrationFor runtime historical data access, consider:
- Using
wrangler d1 time-travel restorefor point-in-time restoration - Implementing application-level versioning with
created_at/updated_atcolumns - Creating separate historical tables with triggers
Choose the package that fits your needs:
| Package | Size | When to Use |
|---|---|---|
| refine-sqlx | 10 KB | Smallest bundle, supports all databases |
| refine-sqlx/d1 | 67 KB | D1-only, includes inlined Drizzle ORM |
Both packages have identical APIs - just different bundle optimizations!
- Fast cold starts - Minimal bundle size
- Type-safe queries - Drizzle ORM type inference
- Atomic transactions - D1 batch API with rollback support
-
Define Schema (
example/schema.ts)export const users = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').notNull(), email: text('email').notNull().unique(), });
-
Generate Migration
bun run db:generate:d1
-
Review Generated SQL (
drizzle/d1/0000_*.sql) -
Apply to Local/Dev
# Using migration script D1_DATABASE_NAME=refine-sqlx-demo-dev bun run db:migrate:d1 # Or using wrangler wrangler d1 execute refine-sqlx-demo-dev --file=./drizzle/d1/0000_*.sql
-
Test Changes
wrangler dev
-
Run Tests
bun test -
Build
bun run build
-
Apply Migrations to Production
# Preview (dry run) DRY_RUN=true D1_DATABASE_NAME=refine-sqlx-demo-prod bun run db:migrate:d1 # Apply for real D1_DATABASE_NAME=refine-sqlx-demo-prod bun run db:migrate:d1
-
Deploy Worker
wrangler deploy --env production
# View database info
wrangler d1 info refine-sqlx-demo
# Execute SQL directly
wrangler d1 execute refine-sqlx-demo --command="SELECT * FROM users LIMIT 5"
# Backup database
wrangler d1 export refine-sqlx-demo --output=backup.sql
# Restore database
wrangler d1 execute refine-sqlx-demo --file=backup.sql
# Open Drizzle Studio for visual database management
bun run db:studio:d1Problem: Migration fails with "table already exists"
Solution:
# Check existing tables
wrangler d1 execute DB --command="SELECT name FROM sqlite_master WHERE type='table'"
# Drop table if needed (be careful!)
wrangler d1 execute DB --command="DROP TABLE IF EXISTS users"Problem: Can't connect to D1 via Drizzle Kit
Solution: Verify environment variables:
echo $CLOUDFLARE_ACCOUNT_ID
echo $CLOUDFLARE_DATABASE_ID
echo $CLOUDFLARE_API_TOKENProblem: "DB is not defined"
Solution: Check wrangler.toml has correct D1 binding:
[[d1_databases]]
binding = "DB" # Must match env.DB in your code
database_name = "refine-sqlx-demo"
database_id = "..."- Always Use Migrations - Don't modify schema directly in production
- Test Locally First - Use
wrangler devbefore deploying - Version Control - Commit all migration files to git
- Backup Before Migration - Use
wrangler d1 exportbefore applying changes - Use Environment Variables - Never hardcode credentials
- Review Generated SQL - Always check migration files before applying