A full-stack blogging platform inspired by Medium, built with a Cloudflare Workers backend using the Hono framework, a React frontend, and a shared NPM package for Zod type validation.
🔗 Backend Live: backend.ronnie100-hld.workers.dev
📦 NPM Package: @rishabh100x/medium-common
┌──────────────────────────────────┐
│ NPM Package │
│ @rishabh100x/medium-common │
│ (Zod type validation schemas) │
└──────────┬───────────────┬────────┘
type │ │ type
Validation │ │ Validation
▼ ▼
┌───────────────┐ auth ┌─────────────────────────┐ queries ┌──────────────────┐ query ┌──────────────────┐
│ ├─────────►│ ├───────────►│ Connection Pool ├────────►│ │
│ React │ │ Cloudflare Backend │ │ (Prisma │ │ Postgres │
│ Frontend │◄─────────┤ (Hono Framework) │ │ Accelerate) │◄────────│ Database │
│ │ response│ │ │ │ schema │ │
└───────────────┘ └─────────────────────────┘ └──────────────────┘ └──────────────────┘
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
password String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}root/
├── backend/ # Cloudflare Worker — Hono + Prisma Accelerate
├── common/ # Shared NPM package — Zod validation schemas
└── frontend/ # React app
| Layer | Technology |
|---|---|
| Frontend | React JS |
| Backend | Hono (Cloudflare Workers) |
| ORM | Prisma |
| Database Connection | Prisma Accelerate (Connection Pooling) |
| Database | PostgreSQL |
| Type Validation | Zod (via shared NPM package) |
| Deployment | Cloudflare Workers |
The common package is published to npm as @rishabh100x/medium-common and consumed by both the frontend and backend to share Zod validation schemas and inferred TypeScript types.
This ensures input validation is consistent across client and server with no duplicated type definitions.
npm install @rishabh100x/medium-commonExample usage:
import { signupInput, SignupInput } from "@rishabh100x/medium-common";
const result = signupInput.safeParse(req.body);
if (!result.success) {
return c.json({ error: "Invalid input" }, 400);
}- Node.js >= 18
- A PostgreSQL database (e.g., Neon, Supabase, Aiven)
- Prisma Accelerate connection string
- Cloudflare account (for Workers deployment)
- Wrangler CLI:
npm install -g wrangler
cd backend
npm installCreate a .env file:
DATABASE_URL="prisma://accelerate.prisma-data.net/?api_key=your_prisma_accelerate_key"
JWT_SECRET="your_jwt_secret"Run locally:
npm run devDeploy to Cloudflare Workers:
npx wrangler deploycd frontend
npm install
npm run devcd common
npm install
npm run buildTo publish a new version:
npm publish --access public- User submits a request (signup / login / create post) on the React frontend
- Input is validated on the frontend using Zod schemas from
@rishabh100x/medium-common - Request hits the Cloudflare Worker (Hono backend)
- Backend re-validates input using the same Zod schemas from the shared package
- Hono routes the query through Prisma Accelerate (connection pooling)
- Prisma Accelerate forwards the query to the PostgreSQL database
- Response travels back to the frontend
- Prisma Accelerate is used instead of a direct Prisma connection because Cloudflare Workers do not support persistent TCP connections. Accelerate provides an HTTP-based connection pool compatible with edge runtimes.
- The shared
commonpackage is the single source of truth for all input validation types, keeping frontend and backend automatically in sync.