Polish Among Us community website built with Next.js 15, TypeScript, and Cloudflare Workers.
- Frontend: Next.js 15 + TypeScript + Tailwind CSS
- Database: Cloudflare D1 (SQLite) with Prisma ORM
- API: Auto-generated OpenAPI 3.0 documentation with CORS support
- Deployment: Cloudflare Workers
- 🔒 Secure: All API endpoints protected with HTTP Basic Auth
- 📊 Type-Safe: Full TypeScript integration with Prisma
- 🚀 Clean: Method separation, co-located schemas
- 📖 Auto-Docs: Interactive Swagger UI at
/api/docs - 🌐 CORS Ready: Cross-origin support for API testing
- Node.js >= 22.13.1
- Cloudflare account with D1 access
-
Install dependencies
npm ci
-
Configure environment
cp .dev.vars.example .dev.vars
-
Setup database
npm run db:migrate:apply:local
-
Start development
npm run dev
This project uses Wrangler CLI + Prisma migrate diff approach for D1 migrations.
- Update Prisma schema in
prisma/schema.prisma - Create migration file:
npm run db:migrate:create feature_name
- Generate SQL from schema changes:
npm run db:migrate:diff -- --output prisma/migrations/0002_feature_name.sql
- Apply locally:
npm run db:migrate:apply:local
- Regenerate Prisma client:
npm run db:generate
npm run db:generate # Generate Prisma client
npm run db:migrate:create <name> # Create new migration file
npm run db:migrate:diff # Generate SQL from schema changes
npm run db:migrate:apply:local # Apply migrations to local D1
npm run db:migrate:apply:preview # Apply migrations to preview D1
npm run db:migrate:apply:remote # Apply migrations to production D1
npm run db:execute:local # Execute SQL on local D1
npm run db:execute:preview # Execute SQL on preview D1
npm run db:execute:remote # Execute SQL on production D1# View all tables
wrangler d1 execute townofus-pl --local --command "SELECT name FROM sqlite_master WHERE type='table';"
# Interactive SQL shell
wrangler d1 execute townofus-pl --local
# Check migration status
wrangler d1 migrations list townofus-pl --local- Interactive Docs: Visit
/api/docsfor Swagger UI - Status Check: Visit
/api/statusfor health check - OpenAPI Schema: Available at
/api/schema
All API endpoints require HTTP Basic Auth:
- Username:
demo_user - Password:
demo_pass
- Global Protection: All API routes secured with HTTP Basic Auth
- CORS Enabled: Cross-origin support for API testing
- Environment-Aware: Different policies for dev vs production
src/app/api/
├── _database/ # Prisma client utilities
├── _utils/ # Response helpers
├── _middlewares/ # Auth + CORS middleware
├── schema/ # Shared schemas & OpenAPI registry
├── docs/ # Swagger UI
├── status/ # Health check endpoint
└── fruits/ # Example CRUD API
├── get.ts # GET /api/fruits
├── post.ts # POST /api/fruits
└── [id]/
├── get.ts # GET /api/fruits/{id}
├── put.ts # PUT /api/fruits/{id}
└── delete.ts # DELETE /api/fruits/{id}
-
Create method file (e.g.,
users/post.ts):import { withAuth, withCors } from '@/app/api/_middlewares'; import { openApiRegistry } from '@/app/api/schema/registry'; // Define & register schema const CreateUserSchema = z.object({ username: z.string().min(1) }); openApiRegistry.register('CreateUser', CreateUserSchema); // Register endpoint openApiRegistry.registerPath({ method: 'post', path: '/api/users', summary: 'Create user', // ... responses }); // Handler with route-level auth & CORS protection export const POST = withCors(withAuth(async (request) => { // Implementation }));
-
Export in route.ts:
export { POST } from './post';
-
Add to schema imports in
schema/route.ts:await import('@/app/api/users/post');
That's it! Route-level wrappers (withAuth, withCors) handle:
- HTTP Basic Auth for all endpoints
- CORS headers automatically
- Cloudflare Workers compatibility
Documentation updates automatically in /api/docs.
npm run dev # Local development (uses local D1)
npm run preview # Preview build (uses preview D1 + auto-migrates)Automated via GitHub Actions on push to main:
- Applies database migrations to production D1
- Builds and deploys to Cloudflare Workers
Required GitHub Secrets:
CLOUDFLARE_API_TOKEN- Cloudflare API token with Workers and D1 permissionsAPI_USERNAME- Basic auth username for API endpointsAPI_PASSWORD- Basic auth password for API endpoints
MIT License