Intro
If you’ve written an even moderately complex web application you know the pain of deploying it on aws or on pretty much any provider if you may ask. Most of us tend to use vercel or netlify because it eases our life so much, so wouldn’t it be great if we could ease the deployment a bit and and keep the power of aws at our fingertips too?
Behold Cloudflare! Cloudflare has been doing some of the great work of in the past few years. Not only they keep adding more & more services to their platform but they also increased the compatibility layer between workers and nodejs api enormously, which means you can migrate your existing application on cloudflare very easily, you don’t have to dig through docs to find that one specific api etc. Cloudflare not only provides you services like serverless functions, d1 database, R2 bucket, Image transformation, bullet proof DNS but all of them are dirt cheap with very generous free tier. So in this blog we explore how to deploy an application to cloudflare.
Where to start
If you’ve never tried cloudflare before i would suggest you to checkout cloudflare pages which is very easy
to setup and you’ll feel right at home if you’ve used vercel or netlify. For more advance use case you can use their cli tool which sets up
everything for you according to your preference eg. which framework you want to use, you can hop in to the terminal and
type pnpm create cloudflare@latest; press enter, select your framework and sit back until it asks you if you want to deploy right away. its that easy!
The end. Thanks for reading.
Nah just kidding. There’s a lot of times you don’t want to use a meta framework and just need to use like a regular express like backend with your own choice of frontend framework, and it could be possible that cloudflare might not support it fully yet. This is where you need to set up manually and this article is for that niche audience.
Let’s Begin
We start by creating a basic directory structure using vite. It’s almost the industry standard for frontend world.
pnpm create vite@latest
note: It does not matter what you choose, its all static assets
You’ll have something like this
├── eslint.config.js
├── index.html
├── package.json
├── public
│ └── vite.svg
├── readme.md
├── src
│ ├── app.css
│ ├── app.tsx
│ ├── assets
│ │ └── react.svg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
Install dependencies. your fronend is done.
Now let’s add workers to it. Run
pnpm install wrangler @cloudflare/vite-plugin @types/node
Let’s see what each of them do:
-
wrangler is the cloudflare cli tool, used to deploy/adding binding to the worker.
-
vite plugin is there to make our life easier it automatically integrates our vite app with workers so we won’t have to define our static assets in the config file at all.
-
node types is there because we’re going to turn on the node compatibility flags and the types are prerequisite.
First open your vite.config.ts and add the cloudflare plugin
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { cloudflare } from '@cloudflare/vite-plugin'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), cloudflare()],
})
Now create wrangler.json in project root directory and paste this:
{
"name": "handroll-worker",
"compatibility_date": "2025-04-05",
"assets": {
"not_found_handling": "single-page-application"
},
"observability": {
"enabled": true
},
"main": "./workerapi/index.ts"
}
-
not_found_handling is there to redirect the user to the home page when there is no route match for the request.
-
main defines the api handler inside the worker.
Now run, pnpm wrangler types to generate cloudflare types so we can add it to the tsconfig.
It will generate a worker-configuration.d.ts file
Now create tsconfig.worker.json and add this to the file
{
"extends": "./tsconfig.node.json",
"compilerOptions": {
"types": [
"vite/client"
]
},
"include": [
"./worker-configuration.d.ts",
"./workerapi"
]
}
Also open tsconfig.json and add this worker config to the path. your tsconfig.json should look something like this.
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.worker.json"
}
],
"compilerOptions": {
"types": [
"worker-configuration.d.ts"
]
}
}
Now create index file in workerapi. Open workerapi/index.ts and add the following content.
export default {
fetch(request) {
const url = new URL(request.url);
if (url.pathname.startsWith("/api/")) {
return Response.json({
name: "Cloudflare",
});
}
return new Response(null, { status: 404 });
},
} satisfies ExportedHandler<Env>;
This defines an “/api” endpoint that returns a json object. We can now call this on our frontend. Create a useState variable:
const [name, setName] = useState('empty')
Then add the fetch handler to get the data from the backend:
<div className='card'>
<button
onClick={() => {
fetch('/api')
.then((res) => res.json() as Promise<{ name: string }>)
.then((data) => setName(data.name))
}}
aria-label='get name'
>
Name from API is: {name}
</button>
</div>
You can now run pnpm run build && pnpm wrangler deploy and it will deploy the worker within seconds.
Technically we’re done here but who likes to create endpoints from scratch like this.
if (url.pathname.startsWith("/api/")) {
return Response.json({
name: "Cloudflare",
});
}
So we’ll add Hono to our worker. Hono is a great express like backend which has first class support for cloudflare workers. Here’s how to do it.
Install Hono using your favourite package manager.
Create a new file in the directory ‘workerapi/hono/index.ts’ create a basic api endpoint:
import { Hono } from "hono";
export const app = new Hono()
.get('/api', (c) => {
return c.json({ name: 'Cloudflare' })
})
Now go to workersapi/index.ts and change its content to:
import { app } from "./hono";
export default {
fetch(request, env, ctx) {
return app.fetch(request, env, ctx)
},
} satisfies ExportedHandler<Env>;
So what is happening here?
We are creating a handler using Hono and exporting it to the workers with the env variables and contexts.
Now you can fire up development server and you should see things working as expected.
Wrap up
So what are the advantages of this setup?
-
If you go to the browser and visit http://localhost:5173/api you’ll be redirected to the home page which is very different from the normal web apps. If you curl the same endpoint on the terminal you’ll see the output as expected.This happens because cloudflare handles your static assets differently. You assets are hosted on their globally hosted CDN which caches everything on frontend and delivers it to user super fast.
-
The ability to add whatever you want. You can add database, r2 bucket, Ai models whatever cloudflare offers you within seconds. All you have to do is add the bindings to the wrangler file and run “pnpm wrangler types”. You’ll have your bindings available within your app through the env variable, all typesafe. Visit docs to see how bindings works. You can also look up to Hono docs.
-
And ofc the modularity. You can add as many library/middleware to your Hono backend (eg. trpc/zod/openapi) or to the frontend. You can even change the entire frontend or backend if you want, now that you know how things work & are put together.
We can do a lot more than this but seems like its enough for today. we’ll explore more in next article.