A production-ready, batteries-included Express.js starter kit built for rapid backend development. This opinionated starter scaffolds your project with pre-configured authentication, notifications, background jobs, file management, and seamless GCP deployment—so you can focus on building features, not infrastructure.
- Why This Starter?
- Tech Stack
- Features
- Architecture Overview
- Prerequisites
- Installation and Setup
- Available Scripts
- Environment Variables
- Deployment
- SDLC Workflow
- Project Structure
- Best Practices
- Troubleshooting
- Roadmap
- Contributing
Building a production-ready backend from scratch involves countless decisions and boilerplate. This starter eliminates that overhead by providing:
- Ready-to-use authentication with JWT, RBAC, and email verification
- Multi-channel notifications (in-app, push, email) out of the box
- Background job processing with Google Cloud Tasks
- Auto-generated API documentation that stays in sync with your code
- Three-environment deployment (dev, test, prod) via GitHub Actions
- Type-safe APIs with TypeScript, TSOA, and Zod validation
- Best practices baked in from folder structure to error handling
Perfect for developers who want to ship features fast without compromising on code quality or scalability.
- TypeScript - Type-safe JavaScript
- Node.js 20 - Runtime environment
- Express.js - Web framework
- TSOA - TypeScript-based OpenAPI generator
- Zod - Schema validation
- PostgreSQL - Relational database
- Xata - Managed PostgreSQL hosting with built-in search and analytics
- Prisma - Modern ORM with type safety
- Cloud Run - Serverless container platform
- Cloud Tasks - Distributed task queue for background jobs
- Cloud Storage - Object storage for files and images
- Artifact Registry - Container image registry
- Cloud Build - Docker image building with caching
- JWT - JSON Web Tokens for stateless authentication
- bcrypt - Password hashing
- hCaptcha - Bot protection for public endpoints
- API Keys - Service-to-service authentication
- Firebase Cloud Messaging (FCM) - Push notifications
- Brevo (SendinBlue) - Transactional email service
- Firestore - As a secondary real-time database
- Multer - File upload middleware
- Sharp - High-performance image processing
- Firebase Storage - Cloud-based file storage
- Infisical - Secret management and environment variables
- ESLint - Code linting
- Prettier - Code formatting
- Husky - Git hooks
- Nodemon - Live code reloading
- Morgan - HTTP request logging
- Flexible Authentication: JWT access/refresh tokens + API keys for service-to-service communication
- Complete Auth Flow: Signup, login, email verification (OTP), password reset, change password
- Role-Based Access Control (RBAC): User, admin, and super-admin roles with hierarchical permissions
- Token Management: Refresh token versioning for multi-device sign-out
- hCaptcha Protection: Bot prevention on sensitive public routes (auto-disabled in local dev)
- Secure Password Handling: bcrypt hashing with automatic salting
- Database Introspection with Prisma: Type-safe queries with auto-completion
- Automatic Schema Migration: Migrate schemas between environments (dev, test, prod) via GitHub Actions
- Managed PostgreSQL: Xata hosting
- Relationship Management: Automatic foreign key handling and cascading deletes
- Automatic Swagger Docs: OpenAPI 3.0 specification auto-generated from TypeScript code
- Interactive API Explorer: Swagger UI at
/docsfor testing endpoints - Shared Types for Models & Validation: Single source of truth for data structures
- Request Validation: Dual-layer validation with TSOA (TypeScript) and Zod (runtime)
- Global Error Handling: Consistent error responses across all endpoints
- Multi-channel Notifications: Unified API for in-app, push (FCM), and email notifications
- Event-Based Notifications: Pre-configured events (SIGN_UP, NEW_POST, GENERAL)
- User/Role Targeting: Send notifications to specific users or all admins
- Unread Count Tracking: Real-time unread notification counts
- Email Templates: Pre-built templates for verification, password reset, and notifications
- Push Notification Management: FCM token registration and device management
- Cloud Tasks Integration: Reliable, scalable background job processing
- Job Status Tracking: Monitor job progress in Firestore
- Built-in Job Types:
- Image resizing with configurable dimensions
- Multi-channel notification sending
- Scheduled Jobs: Delay job execution to a specific time
- Retry Handling: Automatic retries with exponential backoff
- File Upload & Retrieval: Secure uploads to Google Cloud Storage with signed URLs
- Auto Image Resizing: Background processing to generate multiple sizes (thumbnail, small, medium, etc.)
- Image Optimization: Sharp-based compression for optimal file sizes
- Multi-file Support: Upload multiple files in a single request
- MIME Type Validation: Ensure only allowed file types are uploaded
- Three-Environment Architecture: Separate dev, test, and production environments
- CI/CD with GitHub Actions: Automated deployments on branch push
- Docker Multi-stage Builds: Optimized images with layer caching
- Zero-Downtime Deployments: Cloud Run managed rollouts
- Automatic Secret Injection: Infisical integration for secure environment variables
- Health Check Endpoint: Cloud Run health monitoring at
/health
- Live Code Reloading: Instant feedback with Nodemon
- On-the-fly Spec Updates: Swagger docs regenerate automatically
- TypeScript Path Aliases: Clean imports with
#/prefix - One-liner Pagination: Add pagination to any model with
paginatedSortQuery() - One-liner Multi-column Search: Full-text-like search with
withSearch() - Serializers: Transform database models to API responses
- Feature-based Folder Structure: Organized by domain, not technical layer
- Pre-commit Hooks: ESLint and Prettier run automatically on staged files
┌─────────────────────────────────────────────────────────────────┐
│ Client Apps │
│ (Web, Mobile, Third-party) │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Google Cloud Run │
│ (Express.js API Server) │
│ ┌────────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ Auth Module │ │ Post Module │ │ File Module │ │
│ └────────────────┘ └────────────────┘ └─────────────────┘ │
│ ┌────────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ User Module │ │ Notification │ │ BG Jobs │ │
│ └────────────────┘ └────────────────┘ └─────────────────┘ │
└───────────┬──────────────┬─────────────────┬──────────────┬────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Xata/PostgreSQL │ │ Firebase │ │ Cloud Tasks │ │ Brevo │
│ (Main DB) │ │ (Storage, │ │ (Job Queue) │ │ (Email) │
│ - Users │ │ FCM, Jobs)│ │ │ │ │
│ - Posts │ │ │ │ │ │ │
│ - Notifications │ │ │ │ │ │ │
└──────────────────┘ └─────────────┘ └──────────────┘ └──────────────┘
- Client sends request to Cloud Run endpoint
- Express Router matches route to TSOA-generated controller
- Middleware validates JWT/API key and request payload
- Controller processes business logic
- Prisma ORM queries PostgreSQL database via Xata
- Response serialized and returned to client
- Call
bgJobsService.create()from any module to create a background job - Job saved to Firestore with PENDING status
- Cloud Tasks queued with handler endpoint
- Task triggers
/bg-jobs/handlerwith API key - Handler processes job (e.g., resize image, send notification)
- Job status updated to SUCCESS/FAILED in Firestore
- Event trigger (e.g., user signup) calls
notificationService.trigger() - Notification saved to PostgreSQL (in-app)
- Background job created for async processing
- Job sends FCM push to user devices
- Job sends email via Brevo
- User unread count incremented
Before you begin, ensure you have the following set up:
- GCP Project with billing enabled
- Service Account with the following IAM roles:
- Cloud Run Admin
- Cloud Build Editor
- Artifact Registry Writer
- Cloud Tasks Admin
- Storage Admin
- Service Account User
- Enabled APIs:
- Cloud Run API
- Cloud Build API
- Cloud Tasks API
- Cloud Storage API
- Artifact Registry API
- Resources to create:
- Cloud Tasks queues for each environment to hand bg jobs
- Cloud Storage bucket for each environment with public access
Download service account JSON key and save securely.
- Create a Xata account
- Create a new database (PostgreSQL)
- Note your database URL and API key
- Region:
us-east-1(recommended for low latency)
- Create a Firebase project (Use the already create GCP project above)
- Enable Cloud Messaging (FCM) for push notifications
- Create Firestore databaes in native mode for each environment
- Create an Infisical account
- Create a new project
- Create three environments:
dev,test,production - Generate Machine Identity credentials:
- Client ID
- Client Secret
- Project ID
- Add all required environment variables (see Environment Variables)
- Create a Brevo account (formerly SendinBlue)
- Generate an API key
- Verify a sender email address
- Create email templates for verification, password reset, user invitation and notification (if needed)
- Create an hCaptcha account
- Register a site and get:
- Site Key
- Secret Key
- Fork or clone this repository
- Add the following repository secrets (Settings > Secrets):
INFISICAL_CLIENT_IDINFISICAL_CLIENT_SECRETINFISICAL_PROJECT_IDGCP_SA(full GCP service account JSON)
- Node.js 20+ (LTS recommended)
- npm 10+ or yarn
- Git
git clone https://github.com/your-org/rayon-gcp-express-psql-starter.git
cd rayon-gcp-express-psql-starterCreate a .env.infisical file in the project root:
INFISICAL_CLIENT_ID=your-infisical-client-id
INFISICAL_CLIENT_SECRET=your-infisical-client-secret
INFISICAL_PROJECT_ID=your-infisical-project-idImportant: This file is gitignored. Never commit it to version control.
npm installFirst, install the Xata CLI:
npm install @xata.io/cliThen initialize your empty Xata database with the schema from schema.prisma:
npm run schema:initializeThis script will push the schema defined in your local schema.prisma file to your empty Xata database.
Create a super admin user to get started:
npm run create:superadmin -- --email admin@example.com --password your-secure-passwordnpm run devThis script automatically fetches secrets from Infisical and generates a .env file with all environment variables from Infisical. This script also introspects your xata database and updates the schema.prisma file.
The server will start on http://localhost:3000 (or the port specified in your .env file).
Open your browser to:
http://localhost:3000/api/v1/docs
You'll see the interactive Swagger UI where you can test all endpoints.
npm run dev # Start development server with live reload (NODE_ENV=dev)
npm run test # Start test server (NODE_ENV=test)
npm run prod # Start production-like server (NODE_ENV=production)npm run build # Build TypeScript to dist/ folder
npm start # Run production build (node dist/server.js)
npm run setup # Full setup: fetch env, pull schema, generate, tsoanpm run prisma:pull # Pull schema from Xata database
npm run prisma:generate # Generate Prisma Client
npm run prisma:format # Format schema.prisma file
npm run schema-transform # Transform Xata conventions to standard naming
npm run schema:initialize # Initialize new schema
npm run sync-xata-schema # Sync schema changes with Xatanpm run create:superadmin # Create a super admin user with verified email
# Usage: npm run create:superadmin -- -e email -p password [-n name]npm run tsoa # Generate TSOA routes and Swagger specnpm run env:fetch # Fetch secrets from Infisical to .env
npm run env:replace # Replace env variables in config filesLocal Environment Overrides: Create a .env.overrides file (gitignored) to override any Infisical secrets locally without modifying Infisical. Useful for testing with different configurations.
npm run lint # Run ESLint
npm run prettier:check # Check code formatting
npm run prettier:write # Fix code formattingThe following environment variables must be configured in Infisical for each environment (dev, test, production):
GOOGLE_CLOUD_PROJECT=your-gcp-project-id
GOOGLE_APPLICATION_CREDENTIALS=./service_account.json
STORAGE_BUCKET=your-unique-bucket-name
GENERAL_TASKS_QUEUE=projects/your-project/locations/us-east1/queues/generalDATABASE_URL=postgresql://your-xata-url
XATA_API_KEY=xau_your-api-keyACCESS_TOKEN_SECRET=your-long-random-secret
ACCESS_TOKEN_LIFE=8h
REFRESH_TOKEN_SECRET=your-long-random-secret
REFRESH_TOKEN_LIFE=30d
API_KEY_SECRET=your-internal-api-keyHCAPTCHA_SECRET=your-hcaptcha-secret
HCAPTCHA_SITE_KEY=your-hcaptcha-site-keyBREVO_API_KEY=xkeysib-your-api-key
FROM_EMAIL=noreply@yourdomain.comOTP_LIFE=30 # OTP expiration in minutesFIRESTORE_DB_ID=your-firestore-db-idNODE_ENV=env-alias # dev | test | productionSecurity Note: Never commit .env or .env.infisical files. Use Infisical for all environments.
This starter uses a three-environment deployment strategy:
| Environment | Branch | Cloud Run Service | Purpose |
|---|---|---|---|
| Production | main |
rayon-gcp-express-psql-starter-prod |
Live user-facing environment |
| Development | dev |
rayon-gcp-express-psql-starter-dev |
Staging for pre-production verification |
| Test | test |
rayon-gcp-express-psql-starter-test |
Testing environment to deploy and test unfinished features |
All services run on GCP Cloud Run in the us-east1 region.
- Push code to
main,dev, ortestbranch or trigger manually - GitHub Actions workflow triggers:
- Loads secrets from Infisical
- Authenticates with GCP
- Builds Docker image using Cloud Build (with layer caching)
- Pushes image to Artifact Registry
- Deploys to Cloud Run
- Automatically migrates database schema (if PR merge detected)
Access via GitHub Actions tab:
Reload Secrets: Redeploys existing Cloud Run service with updated Infisical secrets
Workflow: reload-secrets.yml
Inputs: environment (dev, test, production)
Migrate Database Schema: Migrate schema between environments
Workflow: migrate-branch.yml
Inputs: baseBranch, targetBranch, lastCommonMigrationId (optional)
- Memory: 1Gi
- CPU: 1
- Max instances: 20
- Min instances: 0 (scales to zero)
- Concurrency: 100 requests per container
- Memory: 512Mi
- CPU: 1
- Max instances: 5
- Min instances: 0
All environments use:
- Port: 8080
- Timeout: 3600s (1 hour)
- Execution environment: gen2
- Authentication: allow-unauthenticated (public access)
- Session affinity: enabled
- Cloud Run Logs: View in GCP Console
- GitHub Actions: Check workflow runs for build/deploy status
- Health Check:
https://your-service-url.run.app/api/v1/health
This section outlines the standard Software Development Lifecycle (SDLC) workflow for developing and deploying features in this project. Following this workflow ensures code quality, proper testing, and smooth collaboration across the team.
The development workflow follows a branch-based strategy with three main environments:
test- Quick testing of under-development featuresdev- Stable staging environment for pre-production verificationmain- Production environment with live user-facing features
Start by creating a new feature branch from the dev branch:
git checkout dev
git pull origin dev
git checkout -b feature/your-feature-nameBranch naming conventions:
feature/feature-name- New featuresfix/bug-description- Bug fixesrefactor/component-name- Code refactoringdocs/description- Documentation updates
If your feature involves database schema changes, create a corresponding Xata branch:
# Create Xata branch from dev
npx xata branch create feature-your-feature-name --from devUpdate your .env.overrides file to point to this Xata branch during development:
DATABASE_URL=your-xata-branch-urlImportant: Always create Xata branches from dev to ensure they have the latest stable schema.
As soon as you have your first commit, push your code and create a draft PR against the dev branch:
git add .
git commit -m "feat: initial implementation of feature X"
git push origin feature/your-feature-nameThen on GitHub:
- Create a Pull Request targeting the
devbranch - Mark it as "Draft" if the feature is not ready for review
- Add a clear description of what the feature does
- Link any related issues
Benefits of early draft PRs:
- Enables early feedback from the team
- Makes your work visible to others
- Prevents merge conflicts by showing what you're working on
- Allows CI/CD checks to run early
If you want to test your feature in a deployed environment (recommended for integration testing):
Step 4a: Migrate Database Schema to Test (if applicable)
If your feature has schema changes, first migrate your Xata feature branch to the test branch:
- Go to GitHub Actions tab in your repository
- Select "Migrate Xata Schema Between Branches" workflow
- Click "Run workflow"
- Fill in the parameters:
- Base branch:
feature-your-feature-name(your Xata branch) - Target branch:
test - Last common migration ID: (leave empty unless you know the specific migration)
- Base branch:
- Click "Run workflow" and wait for completion
Step 4b: Deploy Code to Test
Option A - Create PR against test:
# Create a PR from your feature branch to test and self-mergeOption B - Merge directly to test:
git checkout test
git pull origin test
git merge feature/your-feature-name
git push origin testImportant: Code changes to the test branch will automatically deploy to the test environment via GitHub Actions.
About the Test Environment:
- Used for quickly testing under-development features
- Multiple developers may deploy to test simultaneously
- QA/testers may also use this environment for API testing
- It's okay if test breaks - this is an experimental environment
- Test environment is NOT meant to be stable
While your feature is deployed on test:
- Test your feature in the test environment
- Request code review by converting your draft PR to "Ready for review"
- Address any feedback from reviewers
- Ensure all tests pass
- Fix any issues found during testing
Testing checklist:
- Feature works as expected in test environment
- No breaking changes to existing features
- API documentation is updated (Swagger)
- Error handling is implemented
- Security considerations are addressed
Once your PR is approved by a reviewer and testing is successful:
Step 6a: Migrate Schema to Dev (BEFORE merging code)
If you made database schema changes, run the Xata migration workflow before merging your code:
- Go to GitHub Actions > "Migrate Xata Schema Between Branches"
- Run workflow with:
- Base branch:
feature-your-feature-name - Target branch:
dev
- Base branch:
- Wait for migration to complete successfully
Critical: Schema changes must always be synced to the target branch before syncing code changes.
Step 6b: Merge Your PR
After schema migration is complete, merge your PR:
# Click "Merge Pull Request" on GitHub
# or via CLI:
git checkout dev
git merge feature/your-feature-name
git push origin devStep 6c: Automatic Deployment
Code changes to dev will automatically deploy to the dev environment via GitHub Actions. Monitor the deployment in the Actions tab.
The dev branch should always be in a production-ready state:
- All features on
devshould be fully tested devshould be stable enough to promote tomainat any time- Avoid pushing untested or experimental code to
dev - Use
testbranch for experimentation
Periodically (or at defined release intervals), a team lead or release manager will promote dev to production:
-
Migrate schema from dev to main:
- Run "Migrate Xata Schema Between Branches" workflow
- Base:
dev, Target:main
-
Merge dev to main:
git checkout main git pull origin main git merge dev git push origin main
-
Monitor production deployment:
- Check GitHub Actions for successful deployment
- Monitor Cloud Run logs for any issues
- Verify health check endpoint
If multiple developers are working on different features:
- Each creates their own feature branch from
dev - Each can deploy to
testindependently - The last merge to
testwill be deployed (expected behavior) - PRs to
devare merged sequentially after review
If two features modify the same tables/fields:
- Coordinate with other developers via draft PRs
- Consider rebasing your feature branch on latest
dev - Run schema migrations carefully, checking for conflicts
- Use the
lastCommonMigrationIdparameter if needed
Alternatively, use GitHub's "Revert" button on the merged PR.
- Always create Xata branches for schema changes
- Create draft PRs early to enable collaboration
- Test in
testenvironment before merging todev - Migrate schema BEFORE merging code to target branches
- Keep
devstable and production-ready - Communicate with the team about test environment usage
- Monitor deployments after merging to any environment branch
rayon-gcp-express-psql-starter/
├── .github/workflows/ # CI/CD pipelines for deployment
├── .husky/ # Git hooks (pre-commit linting)
├── .xata/ # Xata database configuration
├── prisma/ # Prisma ORM schema
│ └── schema.prisma # Database models and relations
├── scripts/ # Build and deployment automation scripts
│ ├── load-secrets-ci.ts # Fetch Infisical secrets in CI
│ ├── schema-transform.ts # Transform Xata naming conventions
│ ├── xata-migration.ts # Migrate schemas between environments
│ └── ... # Other utility scripts
├── src/
│ ├── app.ts # Express app configuration
│ ├── server.ts # Entry point (HTTP server)
│ ├── lib/ # Reusable, feature-independent code
│ │ ├── cloud-task/ # Google Cloud Tasks client
│ │ ├── firebase/ # Firebase Admin SDK setup
│ │ ├── mail/ # Brevo email client
│ │ ├── otp/ # OTP generation and verification
│ │ ├── types/ # Shared TypeScript types
│ │ ├── utils/ # Utility functions (pagination, search, etc.)
│ │ └── constants.ts # Application constants
│ ├── middlewares/ # Express middleware
│ │ ├── auth.middleware.ts # JWT and API key authentication
│ │ ├── error.middleware.ts # Global error handler
│ │ └── validation.middleware.ts # Request validation
│ └── modules/ # Feature modules (domain-driven)
│ ├── auth/ # Authentication and authorization
│ │ ├── auth.controller.ts
│ │ ├── auth.service.ts
│ │ ├── auth.types.ts
│ │ └── auth.serializer.ts
│ ├── user/ # User management
│ ├── profile/ # User profiles
│ ├── post/ # Posts module (example entity)
│ ├── file/ # File upload and storage
│ ├── notification/ # Multi-channel notifications
│ ├── bg-jobs/ # Background job processing
│ └── misc/ # Health check, Swagger docs
├── Dockerfile # Multi-stage Docker build
├── cloudbuild.yaml # GCP Cloud Build configuration
├── tsoa.json # TSOA API generator config
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── README.md # This file
Each feature module follows a consistent pattern:
module-name/
├── module-name.controller.ts # TSOA controller (routes + validation)
├── module-name.service.ts # Business logic
├── module-name.types.ts # TypeScript types
└── module-name.serializer.ts # Response transformers
This structure promotes:
- Separation of concerns: Controllers handle HTTP, services handle logic and are reusable
- Testability: Services can be tested independently
- Type safety: Shared types ensure consistency
- Maintainability: Related code stays together
- Use kebab-case for file and folder names (e.g.,
user-profile.service.ts) - Use PascalCase for type names (e.g.,
UserProfile) - Use camelCase for variable and function names (e.g.,
getUserProfile) - Use snake_case for database field names (e.g.,
user_id,created_at)
- Reusable, feature-independent code →
src/lib/folder - Feature-specific code →
src/modules/folder - Middleware →
src/middlewares/folder - Types meant for multiple modules →
src/lib/types/folder
- Foreign key names must follow the format
<name>_id(e.g.,user_id,author_id)- This convention allows schema transform scripts to auto-detect relationships
- Use
snake_casefor all field names - Leverage Prisma relations for type-safe joins
- Use
statusConstobject for HTTP status codes instead of hardcoding numbers - Basic validation → rely on TSOA's TypeScript-based validation
- Advanced validation (email format, min/max, regex) → use Zod schemas
- Wrap complex nested types in the
Expandutility type for TSOA compatibility - Always serialize database models before returning (remove sensitive field, transform objects)
- Use
@Security("jwt")for user-authenticated routes - Use
@Security("api_key")for internal service-to-service routes - Specify required roles via TSOA scopes:
@Security("jwt", ["admin"])
- Use background jobs for:
- Long-running tasks (image processing, data exports)
- Heavy external API calls (ex: push notifications)
- Tasks that can tolerate eventual consistency
- In-app notifications: Always create for important events
- Push notifications: Use for time-sensitive updates
- Email: Use for critical account-related actions (verification, password reset)
- Target specific users or roles, not all users, to avoid spam
- Throw descriptive errors with appropriate HTTP status codes
- Let the global error middleware handle uncaught internal errors (status code: 500) and validation errors (status code: 422)
- Log errors for debugging
- Never commit
.env,.env.infisical, orservice_account.jsonfiles - Use hCaptcha on public endpoints prone to abuse
- Validate and sanitize all user inputs
- Use parameterized queries (Prisma does this automatically)
- Files in
scripts/should have no external dependencies (node built-ins only) - Scripts run before
npm installin some contexts (e.g., Docker build)
Cause: TypeScript path aliases not resolved.
Solution:
npm run build # Rebuilds with alias resolutionCause: Prisma Client out of sync with schema.
Solution:
npm run prisma:generateCause: Invalid DATABASE_URL or Xata database offline.
Solution:
- Verify
DATABASE_URLin.env - Check Xata dashboard for database status
- Ensure Xata API key is valid
Cause: Missing or invalid service_account.json.
Solution:
- Ensure
service_account.jsonexists in project root - Verify JSON structure is valid
- Re-download from Firebase Console if needed
Cause: Invalid queue name or insufficient permissions.
Solution:
- Verify
GENERAL_TASKS_QUEUEformat:projects/PROJECT_ID/locations/REGION/queues/QUEUE_NAMEin infisical stored secret - Ensure service account has "Cloud Tasks Admin" role
- Confirm queue exists in GCP Console
Cause: Invalid site key or secret.
Solution:
- Verify
HCAPTCHA_SECRETandHCAPTCHA_SITE_KEYin Infisical - Check hCaptcha dashboard for site status
- Ensure frontend sends correct
hCaptchaToken
Cause: TSOA routes not regenerated.
Solution:
- Make sure there are no typescript errors
- Sometimes there's no typescript error, but still a tsoa error. If you are using a complex nested type in a controller, make sure to wrap inside Expand type utility.
Cause: Missing secrets or incorrect GCP configuration.
Solution:
- Verify all GitHub secrets are set (see Prerequisites)
- Check workflow logs for specific error
- Ensure GCP service account has required IAM roles
- Confirm Cloud Run service name matches workflow config
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Check the Live Demo for API examples
- Add unit tests: Jest + Supertest for comprehensive test coverage
- Add rate limiting: Protect endpoints from abuse with
express-rate-limit - Add database seeding: Seed scripts for local development
- Add IAC: Auto provision new environments / projects
- Add request logging: Structured logging with request IDs for tracing
- Disaster Recovery: Automated backup and restore procedures for PostgreSQL and Firestore with point-in-time recovery
Want to contribute? See Contributing below!
We welcome contributions from the community! Whether it's bug fixes, new features, or documentation improvements, your help makes this starter better for everyone.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes: Follow the Best Practices above
- Test thoroughly: Ensure all existing functionality still works
- Commit with descriptive messages:
git commit -m "feat: add rate limiting middleware" - Push to your fork:
git push origin feature/your-feature-name - Open a Pull Request: Describe your changes and link any related issues
- Follow the existing code style and conventions
- Write clear commit messages
- Update documentation for any user-facing changes
- Run
npm run lintbefore committing - Ensure Prettier formatting is applied (
npm run prettier:write) - Test changes in at least the
devenvironment before PR - Update the README if you add new features or change behavior
Found a bug or have a feature request? Open an issue with:
- Clear description of the problem or feature
- Steps to reproduce (for bugs)
- Expected vs actual behavior
- Your environment (Node version, OS, etc.)
This project is licensed under the MIT License. See the LICENSE file for details.
Built with love by the Rayon Studios team. Special thanks to all contributors.
Happy coding! If this starter helped you ship faster, consider giving it a star ⭐ on GitHub.