A database is a project-level resource, not a hidden extra on a service. You create one, you link it to the services that should reach it, and you name the link. That name becomes the prefix of the environment variables the service receives, so a link called APP_DB arrives as APP_DB_URL and APP_DB_AUTH_TOKEN.
Nothing is injected behind your back. There is no implicit database and no magic DATABASE_URL, because a variable you did not ask for is a variable you cannot reason about.
const url = Deno.env.get("APP_DB_URL")!; const token = Deno.env.get("APP_DB_AUTH_TOKEN")!; // libsql speaks HTTP: one request, one round trip, no pool. async function query(sql: string) { const response = await fetch(`${url}/v2/pipeline`, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json", }, body: JSON.stringify({ requests: [{ type: "execute", stmt: { sql } }, { type: "close" }], }), }); return await response.json(); } export default { async fetch() { const result = await query("select count(*) as n from visits"); return Response.json(result); }, };
Any libsql or Turso client works too. The raw form is here because it shows the whole protocol: a POST with your statement, an answer, and no long-lived connection in between.
A database can be forked. The copy starts as an exact snapshot of the original and then goes its own way, which is the same trick your editor does with a git branch, applied to data.
That is what makes staging useful here. Your staging environment can run against a fork of production data, so a migration is rehearsed against the real shape of the rows instead of an empty schema, and the original never notices. Promote when it works, throw the branch away when it does not.
Databases are included in the plan rather than metered separately, and the allowance is shared between engines. Manual branches and per-release data states start on Pro.
Every template deploys into your organisation as a real project, live at its own URL. Then it is yours: open the editor and change anything, or tell the AI what you want instead.
It is libsql, the fork of SQLite that added a server. The dialect and the file format are SQLite's; what is different is that you talk to it over HTTP instead of opening a file, which is what makes it usable from a service that sleeps between requests.
No, and that is the point. Each query is an HTTP request against your database's URL, authenticated with its token. Nothing is kept open between requests, so nothing leaks when a service scales to zero.
You link the database to the service and name the link. The name becomes the environment variable prefix: <code>APP_DB</code> gives you <code>APP_DB_URL</code> and <code>APP_DB_AUTH_TOKEN</code>. Unlink it and the variables are gone on the next deploy.
Yes. Fork the database and point the staging environment at the fork. Releases can keep their own data state too, so what a version served is still there when you roll back to it.
On our own machines in the EU, on the same node as your service where possible so a query is a local round trip. Snapshots go to object storage as the durability floor.
Create one. Postgres and Valkey are managed the same way, they link to a service the same way, and nothing stops a project from using all three. Your plan's database allowance is shared between them.
Go beyond what seems possible.