A fullstack monorepo template using pnpm workspaces with Cloudflare Workers for both backend and frontend (TanStack Start with SSR).
.
├── packages/
│ ├── worker/ # Backend Cloudflare Worker (Hono + RPC)
│ └── web/ # Frontend Cloudflare Worker (TanStack Start with SSR)
├── package.json # Root package.json with workspace scripts
└── pnpm-workspace.yaml # pnpm workspace configuration
- Worker RPC: Service bindings enable type-safe RPC calls from web to worker
- Hono API: REST API endpoints in the backend worker
- TanStack Start: Modern React framework with SSR deployed on Cloudflare Workers
- Type Safety: Full TypeScript support across packages
- Monorepo: Shared dependencies and scripts via pnpm workspaces
- Deploy to Workers: Both packages deploy as Cloudflare Workers for optimal performance
- Node.js >= 24
- pnpm (
npm install -g pnpm) - Cloudflare account (for deployment)
-
Customize package names (optional):
The template uses
@fullstack-monorepo-template/*as the package scope. To customize for your project:- Find and replace
@fullstack-monorepo-template/with@your-project-name/across the project - Update
packages/worker/package.jsonname field - Update
packages/web/package.jsonname field - Update the service name in
packages/web/wrangler.jsoncto match your worker name
- Find and replace
-
Install dependencies:
pnpm install- Start the worker in development mode:
pnpm dev:worker- In a separate terminal, start the web app:
pnpm dev:webThe worker will run on http://localhost:8787 and the web app on http://localhost:3000.
pnpm dev- Start the worker in development modepnpm dev:worker- Start the worker in development modepnpm dev:web- Start the web app in development modepnpm test- Run tests in all packagespnpm lint- Lint all packagespnpm lint:fix- Fix linting issues in all packagespnpm format- Format code with Prettierpnpm format:check- Check code formattingpnpm deploy- Deploy all packages
pnpm --filter @fullstack-monorepo-template/worker dev- Start in development modepnpm --filter @fullstack-monorepo-template/worker deploy- Deploy to Cloudflarepnpm --filter @fullstack-monorepo-template/worker test- Run tests
pnpm --filter @fullstack-monorepo-template/web dev- Start in development modepnpm --filter @fullstack-monorepo-template/web build- Build for productionpnpm --filter @fullstack-monorepo-template/web deploy- Deploy to Cloudflare Workers
This project includes two GitHub Actions workflows:
Runs automatically on pull requests to main:
- Lint - Checks code style and formatting (
pnpm lint) - Test - Runs all tests (
pnpm test) - Type Check - Validates TypeScript types for both packages
Runs automatically on push to main:
- Deploy Worker - Deploys backend worker to Cloudflare
- Deploy Web - Deploys frontend worker to Cloudflare (after worker deploys)
To enable automatic deployments, add these secrets to your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Add the following secrets:
CLOUDFLARE_API_TOKEN- Your Cloudflare API token (Create one here)CLOUDFLARE_ACCOUNT_ID- Your Cloudflare account ID
You can find your account ID in your Cloudflare dashboard URL: https://dash.cloudflare.com/<ACCOUNT_ID>
Note: The API token needs Workers Scripts:Edit permissions for deployments.
- Login to Cloudflare:
cd packages/worker
pnpm wrangler login-
Update
wrangler.jsoncwith your account details -
Deploy:
pnpm deploy:workerThe web app is deployed as a Cloudflare Worker (not Pages), which enables server-side rendering and RPC communication with the backend worker.
- Login to Cloudflare (if not already done):
cd packages/web
pnpm wrangler login-
Update
wrangler.jsoncwith your account details -
Deploy:
pnpm deploy:webThe web package can call the worker via RPC using service bindings for type-safe, low-latency communication.
export class WorkerRpc extends WorkerEntrypoint {
async sayHello(name: string) {
return { message: `Hello, ${name}!`, timestamp: Date.now() };
}
}import { createFileRoute } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';
import { getWorkerRpc } from '@/lib/rpc';
const sayHello = createServerFn({ method: 'GET' }).handler(async () => {
const workerRpc = getWorkerRpc();
// Call RPC methods with full type safety
const result = await workerRpc.sayHello('World');
return result;
});
export const Route = createFileRoute('/my-route')({
loader: async () => sayHello(),
component: MyComponent,
});The template includes example RPC routes in packages/web/src/routes/rpc/:
/rpc/say-hello?name=Alice- Returns a greeting message/rpc/calculate?operation=add&a=5&b=3- Performs arithmetic operations/rpc/get-data?key=myKey- Fetches data (can be extended for KV/D1/R2)/rpc/process-batch?items=a,b,c- Processes batch operations
Note: These are template examples - customize or replace them with your own RPC methods for your use case.
See packages/web/src/routes/rpc/README.md for detailed documentation on the RPC architecture and how to add new methods.
packages/worker/wrangler.jsonc- Cloudflare Worker configurationpackages/worker/src/index.ts- Main worker entry point (HTTP handler)packages/worker/src/rpc.ts- RPC entrypoint for service bindings
packages/web/wrangler.jsonc- Cloudflare Workers configuration (includes service binding)packages/web/vite.config.ts- Vite configurationpackages/web/src/routes/- TanStack Start routes
- Monorepo: pnpm workspaces
- Backend Worker: Cloudflare Workers, Hono, WorkerEntrypoint (RPC), TypeScript
- Frontend Worker: Cloudflare Workers, TanStack Start (SSR), React, TypeScript
- Testing: Vitest
- Linting: ESLint, Prettier
We welcome contributions! Please see CONTRIBUTING.md for guidelines on:
- Setting up the development environment
- Code standards and style guidelines
- Testing requirements
- Pull request process
- Monorepo-specific guidelines
MIT License - see LICENSE file for details