Starter SaaS website template, complete with website, api, worker, component library, DB, and more!
- components: UI library; Should depend only on shared
- shared: logic, database, utils for use across all packages; Should not be dependent on other packages
- web: user-interactible web frontend & API server backend; Should not be dependency of other packages
- worker: background, async crons, queues, and other backlend services; Should not be dependency of other packages
Original template was formed from Taxonomy and:
- Next.js as the React framework
- shadcn/ui for the component library
- Tailwind for CSS styling
- Drizzle as the ORM for database access
- Cloudflare D1 as the database (SQLite)
- Firebase for authentication
- Cloudflare Pages for web deployment & APIs
- Cloudflare Workers for Crons, Queus, Webhook handlers, any generally any other async backend/background service
- Axiom for logging
- Webpack via Next.js and Cloudflare workers for JS build system/bundler
- Prettier for code formatting and auto-styling
- ESLint for linting
- nanoid for unique identifiers
FIREBASE_PRIVATE_KEYshould be stripped of all \n. Cloudflare environment variables do not seem to escape/unescape these properly, at least when entered from the dashboard.AUTH_COOKIE_SIGNATURE_1&AUTH_COOKIE_SIGNATURE_2must not contains special characters (likely =, but maybe others)
Use Cloudflare D1 for SQLite databases. Use Drizzle for ORM
-
Id not ID
-
UpperCamelCase: class / interface / type / enum / decorator -
lowerCamelCase: variable / parameter / function / method / property / module alias -
CONSTANT_CASE: global constant values, including enum values
When in doubt, fallback to https://google.github.io/styleguide/tsguide.html
- Prefer
NextRequesttoRequestandNextResponsetoResponseinwebpackage - Prefer
NextResponse.json&&NextResponse.redirectovernew NextResponse - When there's nothing to valuable to send, response with
nulland rely on response codes on the client - For error responses, always response in the format
{ error: "error message" } - Always use
routeHandlerandnoAuthRouteHandler - Omit
{ status: 200 }since that's the default