apps/blog/content/blog/build-applications-at-the-edge-with-prisma-orm-and-cloudflare-d1-preview/index.mdx
Cloudflare has been pioneering the edge computing landscape since the introduction of Workers in 2017. With D1, Cloudflare recently released a native serverless database. We’re excited to announce that you can now talk to D1 using Prisma ORM!
Edge functions, such as Cloudflare Workers, are a form of lightweight serverless compute that's distributed across the globe. They allow you to deploy and run your apps as closely as possible to your end users.
D1 is Cloudflare's native serverless database for edge environments. It's based on SQLite and can be used when deploying applications with Cloudflare. D1 was initially launched in 2022.
<Quotes speakerImgLink="/blog/build-applications-at-the-edge-with-prisma-orm-and-cloudflare-d1-preview/imgs/1200px-Cloudflare_Logo.png" speakerName="D1: our quest to simplify databases (2022)" position="" companyLink="https://blog.cloudflare.com/whats-new-with-d1#transactions-are-a-unique-challenge" companyName="Cloudflare Blog"
You don't need to specify where a Cloudflare Worker or a D1 database runs—they simply run everywhere they need to. </Quotes>
Following Cloudflare's principles of geographic distribution and bringing compute and data closer to application users, D1 supports automatic read-replication: It dynamically manages the number of database instances and locations of read-only replicas based on how many queries a database is getting, and from where.
This means that read-queries are executed against the D1 instance that's closest to the location from where the query was issued.
While you can use read replicas using Prisma ORM with other database providers as well, this typically requires you to use the Read Replica Client extension. When using D1, read replicas are supported out-of-the-box without the need for a dedicated Client extension.
For write-operations, on the other hand, queries still travel to a single primary instance in order to propagate the changes to all read-replicas and ensure data consistency.
At Prisma, we believe that Cloudflare is at the forefront of building the future of how applications are being built and deployed.
You can learn more about how we think about Cloudflare as a partner in improving Data DX in this blog post: Developer Experience Redefined: Prisma & Cloudflare Lead the Way to Data DX
Supporting D1 has been one of the most popular feature requests for Prisma ORM on GitHub.
As a strong believer in Cloudflare as a technology provider, we're thrilled to share that you can now use Prisma ORM inside Cloudflare Workers (and Pages) to access a D1 database.
Note that this feature is based on driver adapters which are currently in Preview, we therefore consider D1 support to be in Preview as well.
In the following, you'll find step-by-step instructions to set up and deploy a Cloudflare Worker with a D1 database that's accessed via Prisma ORM entirely from scratch.
As of this release, Prisma Migrate is not yet fully compatible with D1. In the tutorial, you'll use D1's migration system in combination with the
prisma migrate diffcommand to generate and run migrations.
As a first step, go ahead and use npm create to bootstrap a plain version of a Cloudflare Worker (using Cloudflare's hello-world template). Run the following command in your terminal:
npm create cloudflare@latest prisma-d1-example -- --type hello-world
This will bring up a CLI wizard. Select all the default options by hitting Return every time a question appears.
At the end of the wizard, you should have a deployed Cloudflare Worker at the domain https://prisma-d1-example.USERNAME.workers.dev which simply renders "Hello World" in the browser:
With your Worker in place, let's go ahead and set up Prisma ORM.
First, navigate into the project directory and install the Prisma CLI:
cd prisma-d1-example
npm install prisma --save-dev
Next, install the Prisma Client package as well as the driver adapter for D1:
npm install @prisma/client
npm install @prisma/adapter-d1
Finally, bootstrap the files required by Prisma ORM using the following command:
npx prisma init --datasource-provider sqlite
This command did two things:
prisma that contains your Prisma schema file..env file which is typically used to configure environment variables that will be read by the Prisma CLI.In this tutorial, you won't need the .env file since the connection between Prisma ORM and D1 will happen through a binding. You'll find instructions for setting up this binding in the next step.
Since you'll be using the driver adapter feature which is currently in Preview, you need to explicitly enable it via the previewFeatures field on the generator block.
Open your schema.prisma file and adjust the generator block to look as follows:
prisma generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
In this step, you'll set up your D1 database. There generally are two approaches to this. Either using the Cloudflare Dashboard UI or via the wrangler CLI. You'll use the CLI in this tutorial.
Open your terminal and run the following command:
npx wrangler d1 create prisma-demo-db
If everything went well, you should see an output similar to this:
✅ Successfully created DB 'prisma-demo-db' in region EEUR
Created your database using D1's new storage backend. The new storage backend is not yet recommended for production workloads, but backs up your data via
point-in-time restore.
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "prisma-demo-db"
database_id = "__YOUR_D1_DATABASE_ID__"
You now have a D1 database in your Cloudflare account with a binding to your Cloudflare Worker.
Copy the last part of the command output and paste it into your wrangler.toml file. It should look similar to this:
name = "prisma-d1-example"
main = "src/index.ts"
compatibility_date = "2024-03-20"
compatibility_flags = ["nodejs_compat"]
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "prisma-demo-db"
database_id = "__YOUR_D1_DATABASE_ID__"
Note that __YOUR_D1_DATABASE_ID__ in the snippet above is a placeholder that should be replaced with the database ID of your own D1 instance. If you weren't able to grab this ID from the terminal output, you can also find it in the Cloudflare Dashboard or by running npx wrangler d1 info prisma-demo-db in your terminal.
Next, you'll create a database table in the database in order to be able to send some queries to D1 using Prisma ORM.
D1 comes with its own migration system via the wrangler d1 migrate commands. This migration system plays nicely together with the Prisma CLI which provides tools that allow you to generate SQL statements for schema changes. So you can:
In the following, you'll use both D1's migration system and the Prisma CLI to create and run a migration against your database.
First, create a new migration using the wrangler CLI:
npx wrangler d1 migrations create prisma-demo-db create_user_table
When prompted if the command can create a new folder called migrations, hit Return to confirm.
The command has now created a new directory called migrations and an empty file called 0001_create_user_table.sql inside of it:
migrations/
└── 0001_create_user_table.sql
Next, you need to add the SQL statement that will create a User table to that file. Open the schema.prisma file and add the following User model to it:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
Now, run the following command in your terminal to generate the SQL statement that creates a User table equivalent to the User model above:
npx prisma migrate diff --from-empty --to-schema-datamodel ./prisma/schema.prisma --script --output migrations/0001_create_user_table.sql
This stores a SQL statement to create a new User table in your migration file migrations/0001_ceate_user_table.sql from before, here is what it looks like:
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT NOT NULL,
"name" TEXT
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
You now need to use the wrangler d1 migrations apply command to send this SQL statement to D1. This command accepts two options:
--local: Executes the statement against a local version of D1. This local version of D1 is a SQLite database file that'll be located in the .wrangler/state directory of your project. This approach is useful, when you want to develop and test your Worker on your local machine. Learn more in the Cloudflare docs.--remote: Executes the statement against your remote version of D1. This version is used by your deployed Cloudflare Workers. Learn more in the Cloudflare docs.In this tutorial, you’ll do both: test the Worker locally and deploy it afterwards. So, you need to run both commands. Open your terminal and paste the following commands.
First, execute the schema changes against your local database:
npx wrangler d1 migrations apply prisma-demo-db --local
Next, against the remote database:
npx wrangler d1 migrations apply prisma-demo-db --remote
Hit Return both times when you're prompted to confirm that the migration should be applied.
Both your local and remote D1 instances now contain User table.
Let’s also create some dummy data that we can query once the Worker is running. This time, you’ll run the SQL statement without storing it in a file.
Again, run the command against your local database first:
npx wrangler d1 execute prisma-demo-db --command "INSERT INTO \"User\" (\"email\", \"name\") VALUES
('[email protected]', 'Jane Doe (Local)');" --local
Finally, run it against your remote database:
npx wrangler d1 execute prisma-demo-db --command "INSERT INTO \"User\" (\"email\", \"name\") VALUES
('[email protected]', 'Jane Doe (Remote)');" --remote
You now have a dummy record in both your local and remote database instances. You can find the local SQLite file in .wrangler/state while the remote one can be inspected in your Cloudflare Dashboard.
In order to query your database from the Worker using Prisma ORM, you need to:
DB to the Env interface.PrismaClient using the PrismaD1 driver adapter.Open src/index.ts and replace the entire content with the following:
import { PrismaClient } from '@prisma/client'
import { PrismaD1 } from '@prisma/adapter-d1'
export interface Env {
DB: D1Database
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const adapter = new PrismaD1(env.DB)
const prisma = new PrismaClient({ adapter })
const users = await prisma.user.findMany()
const result = JSON.stringify(users)
return new Response(result);
},
};
Before running the Worker, you need to generate Prisma Client with the following command:
npx prisma generate
With the database query in place and Prisma Client generated, you can go ahead and run the Worker locally:
npm run dev
Now you can open your browser at http://localhost:8787 to see the result of the database query:
[{"id":1,"email":"[email protected]","name":"Jane Doe (Local)"}]
To deploy the Worker, run the the following command:
npm run deploy
As before, your deployed Worker is accessible via https://prisma-d1-example.USERNAME.workers.dev. If you navigate your browser to that URL, you should see the following data that's queried from your remote D1 database:
Congratulations, you just deployed a Cloudflare Worker using D1 as a database and querying it via Prisma ORM 🎉
We would love to hear what you think of the new D1 support in Prisma ORM! Please try it out and share your feedback with us on GitHub or on Discord. Happy coding ✌️