# React SPA with APIs on Cloudflare Worker

Cloudflare is amazing. Think of it as AWS’s thrifty cousin: it can do almost everything, but won’t drain your wallet. Then there’s Vercel, the flashy nephew in a sports car — a fun free ride, but only if you play by their rules.

---

## Options

### Vercel Functions

I tried a Vite React SPA with Vercel Functions for APIs, and it was almost too easy. Drop an endpoint `.ts` file in the `api` folder and it just works — no extra setup.  
Still, Vercel feels like Heroku: trendy, convenient, but risky for a long-term relationship. I don’t want to be locked into their ecosystem.

### AWS

I got burned by AWS in college — surprise bills and high usage costs. That was over a decade ago, but it left a scar.AWS has the “premium“ image, but for me it’s a no-go.

### Google Cloud Run

@[Pixel](@pix-el) suggested Google Cloud Run. But my history with Google App Engine wasn’t great — the app I built no longer runs because the frameworks were all decommissioned. Competitive pricing aside, I don’t want to step into Google’s ecosystem again.

### Fly.io

Fly.io is neat: you provide a Docker image, and they’ll run it. But large images timed out for me, and the community feels smaller. I decided to skip it.

### Cloudflare Workers

Now to my favorite: **Cloudflare**. It offers almost everything you need for SaaS infra at a fraction of AWS’s cost. Generous free tier, solid docs, and a company that’s been strong since 2004.  
Their pricing is straightforward — more like Amazon retail than AWS. The Cloudflare brand stands for stability, consistency, and value.

The UI may look a little old-school, but that actually gives it a charming, no-frills vibe. No excessive `border-radius` here.

When I discovered Cloudflare Workers could handle both SPA hosting and API endpoints, I knew I had to try it.

---

## Set Up Guide

The guide I followed:  
[https://developers.cloudflare.com/workers/vite-plugin/tutorial/](https://developers.cloudflare.com/workers/vite-plugin/tutorial/)

It’s exactly what I needed. Deploying an SPA with minimal APIs and high availability felt simple.

You install `@cloudflare/vite-plugin` and add it to `vite.config.ts`. After that, you can use the usual `vite` dev server. No ecosystem-specific commands like `vercel dev` or `next dev`.  
I like that — it’s just a plugin, not a lock-in.

```typescript
// vite.config.ts
export default defineConfig({
  plugins: […, cloudflare()]
})
```

Now, just run `npm run dev`. Your SPA, APIs, HMR, and React Refresh all work. The API even runs inside a proper V8 runtime like production.

You’ll need `wrangler` for deployments and a `wrangler.jsonc` config:

```json
{
  "name": "sprite-dx",                               // Project name
  "compatibility_date": "2025-08-20",                // Just use today's date
  "assets": {
    "directory": "dist",                             // SPA to use when deployed.
    "not_found_handling": "single-page-application", // This routes all non-matching urls to the SPA
    "run_worker_first": ["/api", "/api/*"]           // Route /api/* to worker
  },
  "main": "./worker/index.ts"                        // API endpoint entrypoint.
}
```

Then add a worker-specific `tsconfig.json`:

```json
// conf/tsconfig.worker.json 
{
  "extends": "./tsconfig.node.json",
  "compilerOptions": {
    "tsBuildInfoFile": "../node_modules/.tmp/tsconfig.worker.tsbuildinfo",
    "types": ["@cloudflare/workers-types/2023-07-01", "vite/client"],
  },
  "include": ["../worker"],
}

// tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./conf/tsconfig.app.json" },
    { "path": "./conf/tsconfig.node.json" },
    { "path": "./conf/tsconfig.worker.json" }   // added
  ]
}
```

And your worker entrypoint:

```typescript
// worker/index.ts
import type { ExportedHandler } from '@cloudflare/workers-types/2023-07-01';

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;
```

At this point, `npm run dev` gives you a working SPA + API locally.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755753249250/db1229f6-71ab-4ec9-9a55-af313d2ab429.png align="center")

---

## Deployment

In `package.json`, add:

```diff
   "scripts": {
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
     "preview": "vite preview",
+    "deploy": "npm run build && wrangler deploy"
   },
```

Deploy with:

```bash
npm run deploy
```

You don’t even need to create a project beforehand — the first deploy sets it up automatically. You’ll find it under **Cloudflare &gt; Compute (Workers) &gt; Workers & Pages**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755753377689/d3cbd9ca-1b57-4050-888d-6f005ed204e3.png align="center")

---

Yeah, the whole experience has been smooth. Next for me: adding environment variables, connecting it to RunPod Serverless with Comfy installed, and testing a full end-to-end image generation workflow at [app.spritedx.com](https://app.spritedx.com).

— Sprited Dev 🌱
