Skip to content

Commit b4a739f

Browse files
authored
Add tutorial for express (#25995)
* add tutorial for express * add deploy button, use GitHub component, and add link * minor fixes
1 parent fcccf65 commit b4a739f

File tree

1 file changed

+391
-0
lines changed

1 file changed

+391
-0
lines changed
Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
---
2+
reviewed: 2025-10-21
3+
difficulty: Beginner
4+
pcx_content_type: tutorial
5+
title: Deploy an Express.js application on Cloudflare Workers
6+
products: [workers, d1]
7+
tags:
8+
- TypeScript
9+
description: >-
10+
Learn how to deploy an Express.js application on Cloudflare Workers.
11+
---
12+
13+
import {
14+
Render,
15+
WranglerConfig,
16+
PackageManagers,
17+
GitHubCode,
18+
} from "~/components";
19+
20+
In this tutorial, you will learn how to deploy an [Express.js](https://expressjs.com/) application on Cloudflare Workers using the [Cloudflare Workers platform](/workers/) and [D1 database](/d1/). You will build a Members Registry API with basic Create, Read, Update, and Delete (CRUD) operations. You will use D1 as the database for storing and retrieving member data.
21+
22+
<Render file="tutorials-before-you-start" product="workers" />
23+
24+
## Quick start
25+
26+
If you want to skip the steps and get started quickly, select **Deploy to Cloudflare** below.
27+
28+
[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/docs-examples/tree/main/workers/express-on-workers)
29+
30+
This creates a repository in your GitHub account and deploys the application to Cloudflare Workers. Use this option if you are familiar with Cloudflare Workers, and wish to skip the step-by-step guidance.
31+
32+
You may wish to manually follow the steps if you are new to Cloudflare Workers.
33+
34+
## 1. Create a new Cloudflare Workers project
35+
36+
Use [C3](https://developers.cloudflare.com/learning-paths/workers/get-started/c3-and-wrangler/#c3), the command-line tool for Cloudflare's developer products, to create a new directory and initialize a new Worker project:
37+
38+
<PackageManagers
39+
type="create"
40+
pkg="cloudflare@latest"
41+
args={"express-d1-app"}
42+
/>
43+
44+
<Render
45+
file="c3-post-run-steps"
46+
product="workers"
47+
params={{
48+
category: "hello-world",
49+
type: "Worker only",
50+
lang: "TypeScript",
51+
}}
52+
/>
53+
54+
Change into your new project directory:
55+
56+
```sh frame="none"
57+
cd express-d1-app
58+
```
59+
60+
## 2. Install Express and dependencies
61+
62+
In this tutorial, you will use [Express.js](https://expressjs.com/), a popular web framework for Node.js. To use Express in a Cloudflare Workers environment, install Express along with the necessary TypeScript types:
63+
64+
<PackageManagers type="add" pkg="express @types/express" />
65+
66+
Express.js on Cloudflare Workers requires the `nodejs_compat` [compatibility flag](/workers/configuration/compatibility-flags/). This flag enables Node.js APIs and allows Express to run on the Workers runtime. Add the following to your `wrangler.toml` file:
67+
68+
<WranglerConfig>
69+
70+
```toml
71+
compatibility_flags = ["nodejs_compat"]
72+
```
73+
74+
</WranglerConfig>
75+
76+
## 3. Create a D1 database
77+
78+
You will now create a D1 database to store member information. Use the `wrangler d1 create` command to create a new database:
79+
80+
```sh frame="none"
81+
npx wrangler d1 create members-db
82+
```
83+
84+
The command will create a new D1 database and ask you the following questions:
85+
86+
- **Would you like Wrangler to add it on your behalf?**: Type `Y`.
87+
- **What binding name would you like to use?**: Type `DB` and press Enter.
88+
- **For local dev, do you want to connect to the remote resource instead of a local resource?**: Type `N`.
89+
90+
```sh output
91+
⛅️ wrangler 4.44.0
92+
───────────────────
93+
✅ Successfully created DB 'members-db' in region WNAM
94+
Created your new D1 database.
95+
96+
To access your new D1 Database in your Worker, add the following snippet to your configuration file:
97+
{
98+
"d1_databases": [
99+
{
100+
"binding": "members_db",
101+
"database_name": "members-db",
102+
"database_id": "<unique-ID-for-your-database>"
103+
}
104+
]
105+
}
106+
✔ Would you like Wrangler to add it on your behalf? … yes
107+
✔ What binding name would you like to use? … DB
108+
✔ For local dev, do you want to connect to the remote resource instead of a local resource? … no
109+
```
110+
111+
The binding will be added to your wrangler configuration file.
112+
113+
<WranglerConfig>
114+
115+
```toml
116+
[[d1_databases]]
117+
binding = "DB"
118+
database_name = "members-db"
119+
database_id = "<unique-ID-for-your-database>"
120+
```
121+
122+
</WranglerConfig>
123+
124+
## 4. Create database schema
125+
126+
Create a directory called `schemas` in your project root, and inside it, create a file called `schema.sql`:
127+
128+
<GitHubCode
129+
repo="cloudflare/docs-examples"
130+
file="workers/express-on-workers/schemas/schema.sql"
131+
commit="4aa8ec65004ab31112a326524a530e98af872787"
132+
lines="1-13"
133+
lang="sql"
134+
code={{
135+
title: "schemas/schema.sql",
136+
}}
137+
/>
138+
139+
This schema creates a `members` table with an auto-incrementing ID, name, email, and join date fields. It also inserts three sample members.
140+
141+
Execute the schema file against your D1 database:
142+
143+
```sh frame="none"
144+
npx wrangler d1 execute members-db --file=./schemas/schema.sql
145+
```
146+
147+
The above command creates the table in your local development database. You will deploy the schema to production later.
148+
149+
## 5. Initialize Express application
150+
151+
Update your `src/index.ts` file to set up Express with TypeScript. Replace the file content with the following:
152+
153+
```ts title="src/index.ts"
154+
import { env } from "cloudflare:workers";
155+
import { httpServerHandler } from "cloudflare:node";
156+
import express from "express";
157+
158+
const app = express();
159+
160+
// Middleware to parse JSON bodies
161+
app.use(express.json());
162+
163+
// Health check endpoint
164+
app.get("/", (req, res) => {
165+
res.json({ message: "Express.js running on Cloudflare Workers!" });
166+
});
167+
168+
app.listen(3000);
169+
export default httpServerHandler({ port: 3000 });
170+
```
171+
172+
This code initializes Express and creates a basic health check endpoint. The key import `import { env } from "cloudflare:workers"` allows you to access [bindings](/workers/runtime-apis/bindings/) like your D1 database from anywhere in your code. The [httpServerHandler](/workers/runtime-apis/nodejs/http/#httpserverhandler) integrates Express with the Workers runtime, enabling your application to handle HTTP requests on Cloudflare's network.
173+
174+
Next, execute the typegen command to generate type definitions for your Worker environment:
175+
176+
```sh frame="none"
177+
npm run cf-typegen
178+
```
179+
180+
## 6. Implement read operations
181+
182+
Add endpoints to retrieve members from the database. Update your `src/index.ts` file by adding the following routes after the health check endpoint:
183+
184+
<GitHubCode
185+
repo="cloudflare/docs-examples"
186+
file="workers/express-on-workers/src/index.ts"
187+
commit="4aa8ec65004ab31112a326524a530e98af872787"
188+
lines="15-41"
189+
lang="typescript"
190+
code={{
191+
title: "src/index.ts",
192+
}}
193+
/>
194+
195+
These routes use the D1 binding (`env.DB`) to prepare SQL statements and execute them. Since you imported `env` from `cloudflare:workers` at the top of the file, it is accessible throughout your application. The `prepare`, `bind`, and `all` methods on the D1 binding allow you to safely query the database. Refer to [D1 Workers Binding API](/d1/worker-api/) for all available methods.
196+
197+
## 7. Implement create operation
198+
199+
Add an endpoint to create new members. Add the following route to your `src/index.ts` file:
200+
201+
<GitHubCode
202+
repo="cloudflare/docs-examples"
203+
file="workers/express-on-workers/src/index.ts"
204+
commit="f223061b9d1717905154980c200bf82263b43aee"
205+
lines="113-164"
206+
lang="typescript"
207+
code={{
208+
title: "src/index.ts",
209+
}}
210+
/>
211+
212+
This endpoint validates the input, checks the email format, and inserts a new member into the database. It also handles duplicate email addresses by checking for unique constraint violations.
213+
214+
## 8. Implement update operation
215+
216+
Add an endpoint to update existing members. Add the following route to your `src/index.ts` file:
217+
218+
<GitHubCode
219+
repo="cloudflare/docs-examples"
220+
file="workers/express-on-workers/src/index.ts"
221+
commit="f223061b9d1717905154980c200bf82263b43aee"
222+
lines="52-111"
223+
lang="typescript"
224+
code={{
225+
title: "src/index.ts",
226+
}}
227+
/>
228+
229+
This endpoint allows updating either the name, email, or both fields of an existing member. It builds a dynamic SQL query based on the provided fields.
230+
231+
## 9. Implement delete operation
232+
233+
Add an endpoint to delete members. Add the following route to your `src/index.ts` file:
234+
235+
<GitHubCode
236+
repo="cloudflare/docs-examples"
237+
file="workers/express-on-workers/src/index.ts"
238+
commit="f223061b9d1717905154980c200bf82263b43aee"
239+
lines="166-185"
240+
lang="typescript"
241+
code={{
242+
title: "src/index.ts",
243+
}}
244+
/>
245+
246+
This endpoint deletes a member by their ID and returns an error if the member does not exist.
247+
248+
## 10. Test locally
249+
250+
Start the development server to test your API locally:
251+
252+
```sh frame="none"
253+
npm run dev
254+
```
255+
256+
The development server will start, and you can access your API at `http://localhost:8787`.
257+
258+
Open a new terminal window and test the endpoints using `curl`:
259+
260+
```sh title="Get all members"
261+
curl http://localhost:8787/api/members
262+
```
263+
264+
```json output
265+
{
266+
"success": true,
267+
"members": [
268+
{
269+
"id": 1,
270+
"name": "Alice Johnson",
271+
"email": "[email protected]",
272+
"joined_date": "2024-01-15"
273+
},
274+
{
275+
"id": 2,
276+
"name": "Bob Smith",
277+
"email": "[email protected]",
278+
"joined_date": "2024-02-20"
279+
},
280+
{
281+
"id": 3,
282+
"name": "Carol Williams",
283+
"email": "[email protected]",
284+
"joined_date": "2024-03-10"
285+
}
286+
]
287+
}
288+
```
289+
290+
Test creating a new member:
291+
292+
```sh title="Create a member"
293+
curl -X POST http://localhost:8787/api/members \
294+
-H "Content-Type: application/json" \
295+
-d '{"name": "David Brown", "email": "[email protected]"}'
296+
```
297+
298+
```json output
299+
{
300+
"success": true,
301+
"message": "Member created successfully",
302+
"id": 4
303+
}
304+
```
305+
306+
Test getting a single member:
307+
308+
```sh title="Get a member by ID"
309+
curl http://localhost:8787/api/members/1
310+
```
311+
312+
Test updating a member:
313+
314+
```sh title="Update a member"
315+
curl -X PUT http://localhost:8787/api/members/1 \
316+
-H "Content-Type: application/json" \
317+
-d '{"name": "Alice Cooper"}'
318+
```
319+
320+
Test deleting a member:
321+
322+
```sh title="Delete a member"
323+
curl -X DELETE http://localhost:8787/api/members/4
324+
```
325+
326+
## 11. Deploy to Cloudflare Workers
327+
328+
Before deploying to production, execute the schema file against your remote (production) database:
329+
330+
```sh frame="none"
331+
npx wrangler d1 execute members-db --remote --file=./schemas/schema.sql
332+
```
333+
334+
Now deploy your application to the Cloudflare network:
335+
336+
```sh frame="none"
337+
npm run deploy
338+
```
339+
340+
```sh output
341+
⛅️ wrangler 4.44.0
342+
───────────────────
343+
Total Upload: 1743.64 KiB / gzip: 498.65 KiB
344+
Worker Startup Time: 48 ms
345+
Your Worker has access to the following bindings:
346+
Binding Resource
347+
env.DB (members-db) D1 Database
348+
349+
Uploaded express-d1-app (2.99 sec)
350+
Deployed express-d1-app triggers (5.26 sec)
351+
https://<your-subdomain>.workers.dev
352+
Current Version ID: <version-id>
353+
```
354+
355+
After successful deployment, Wrangler will output your Worker's URL.
356+
357+
## 12. Test production deployment
358+
359+
Test your deployed API using the provided URL. Replace `<your-worker-url>` with your actual Worker URL:
360+
361+
```sh title="Test production API"
362+
curl https://<your-worker-url>/api/members
363+
```
364+
365+
You should see the same member data you created in the production database.
366+
367+
Create a new member in production:
368+
369+
```sh title="Create a member in production"
370+
curl -X POST https://<your-worker-url>/api/members \
371+
-H "Content-Type: application/json" \
372+
-d '{"name": "Eva Martinez", "email": "[email protected]"}'
373+
```
374+
375+
Your Express.js application with D1 database is now running on Cloudflare Workers.
376+
377+
## Conclusion
378+
379+
In this tutorial, you built a Members Registry API using Express.js and D1 database, then deployed it to Cloudflare Workers. You implemented full CRUD operations (Create, Read, Update, Delete) and learned how to:
380+
381+
- Set up an Express.js application for Cloudflare Workers
382+
- Create and configure a D1 database with bindings
383+
- Implement database operations using D1's prepared statements
384+
- Test your API locally and in production
385+
386+
## Next steps
387+
388+
- Learn more about [D1 database features](/d1/)
389+
- Explore [Workers routing and middleware](/workers/runtime-apis/)
390+
- Add authentication to your API using [Workers authentication](/workers/runtime-apis/handlers/)
391+
- Implement pagination for large datasets using [D1 query optimization](/d1/worker-api/)

0 commit comments

Comments
 (0)