D1
A real SQL database you can spin up in seconds — no servers to run.
What is D1?
D1 is a serverless SQL database (SQL = the standard language for storing data in tables of rows and columns; serverless = you never set up or manage a database server yourself). It runs on SQLite, a tiny, rock-solid database engine used in billions of devices.
You define tables, write familiar SQL queries (SELECT, INSERT, UPDATE), and D1 handles the storage, backups, and scaling for you. It is designed to work hand-in-hand with Cloudflare Workers.
Think of it like…
A super-powered spreadsheet that lives in the cloud. Each table is a sheet with named columns, every row is one record, and SQL is how you ask precise questions like 'show me all customers in Taipei'.
Why use D1?
Running your own database means provisioning servers, patching them, and paying even when idle. D1 removes all of that and bills only for the queries and storage you actually use.
Pay per query
No idle server costs — you pay for rows read/written and storage, nothing else.
Thousands of databases
Create a separate database per customer or project at no extra base cost.
Time Travel backups
Restore your database to any point in the last 30 days if something goes wrong.
Standard SQL
If you know SQLite or basic SQL, you already know how to use D1.
When should you use it?
App data with relations
Users, posts, orders — data where rows relate to each other and you query with SQL.
Per-tenant databases
Give each customer their own isolated database for clean separation.
Side projects & MVPs
The generous free tier makes it ideal for prototypes and small apps.
Full-stack on Workers
Pair D1 with Workers so your API and database live in the same platform.
How do you start?
These steps create a database, define a table, run a query from the terminal, and read it from a Worker. The --remote flag means 'run against the real cloud database' (leave it off to test locally).
Create a database
Wrangler prints a database_id — copy it for the next step.
npx wrangler d1 create my-app-dbBind the database
Add this to wrangler.jsonc so your Worker can reach it as env.DB.
{ "d1_databases": [ { "binding": "DB", "database_name": "my-app-db", "database_id": "<paste-your-id-here>" } ] }Create a table & add data
Run SQL straight from the terminal with --command.
npx wrangler d1 execute my-app-db --remote \ --command "CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, city TEXT);" npx wrangler d1 execute my-app-db --remote \ --command "INSERT INTO customers (name, city) VALUES ('Mei', 'Taipei');"Query from a Worker
prepare() builds a safe query, bind() fills in the ? placeholder (this prevents SQL injection), and all() returns the rows.
export default { async fetch(request, env) { const { results } = await env.DB .prepare("SELECT * FROM customers WHERE city = ?") .bind("Taipei") .all(); return Response.json(results); }, };Deploy
Publish the Worker and your database-backed API is live.
npx wrangler deploy
Key concepts
Table, row, column
A table holds rows (records); each column is a named field like name or city.
Prepared statements
Using prepare() + bind() keeps user input out of the SQL text, blocking injection attacks.
Local vs remote
Without --remote you query a local copy for fast testing; with it you hit the real cloud database.
Time Travel
D1 keeps history so you can roll back to any moment in the past 30 days.
Tips & pricing
Billed by rows, not time
D1 counts rows read and rows written, plus storage. The free tier gives 5 million rows read and 100,000 rows written per day, and 5 GB of storage.
- Add indexes on columns you filter by often to read fewer rows (and save money).
- Keep schema changes in a schema.sql file and apply with --file=./schema.sql.
- Paid plan includes 25 billion rows read and 50 million rows written per month before extra charges.
- Use D1 for relational data; use KV for simple key lookups and R2 for large files.
Related products
menu_bookOfficial docsopen_in_new