ProjectsParts Services
New Service Template - CROP Microservices
Last Updated: 2025-11-19 Purpose: Guide for creating new microservices in CROP monorepo
New Service Template - CROP Microservices
Last Updated: 2025-11-19 Purpose: Guide for creating new microservices in CROP monorepo
Table of Contents
Quick Start
Create New Service (5 minutes)
# 1. Navigate to services directory
cd /Users/vova/Code/CROP/microservices/services
# 2. Create service directory
mkdir my-new-service
cd my-new-service
# 3. Initialize Bun project
bun init -y
# 4. Install dependencies
bun add hono @hono/zod-openapi zod
bun add -d @types/bun
# 5. Create basic structure
mkdir -p src/{routes,middlewares,utils}
touch src/index.ts src/routes/health.tsService Structure
Recommended Directory Layout
services/my-new-service/
├── src/
│ ├── index.ts # Entry point
│ ├── routes/
│ │ ├── health.ts # Health check endpoint
│ │ └── api.ts # API routes
│ ├── middlewares/
│ │ ├── logger.ts # Request logging
│ │ └── auth.ts # Authentication
│ ├── utils/
│ │ └── helpers.ts # Utility functions
│ ├── types/
│ │ └── index.ts # TypeScript types
│ └── config/
│ └── env.ts # Environment configuration
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Local development
├── cloudbuild.yaml # GCP Cloud Build config
├── .env.example # Example environment variables
├── .dockerignore # Docker ignore patterns
├── package.json # Dependencies
├── tsconfig.json # TypeScript configuration
├── CLAUDE.md # Service documentation
└── README.md # Quick referenceRequired Files
1. package.json
{
"name": "@crop/my-new-service",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "bun run --watch src/index.ts",
"start": "bun run src/index.ts",
"build": "bun build src/index.ts --target=bun --outdir=./dist",
"test": "bun test",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"hono": "^4.x.x",
"@hono/zod-openapi": "^0.x.x",
"zod": "^3.x.x"
},
"devDependencies": {
"@types/bun": "latest",
"bun-types": "latest"
}
}2. src/index.ts (Entry Point)
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { compress } from 'hono/compress'
import healthRoutes from './routes/health'
const app = new Hono()
// Middlewares
app.use('*', logger())
app.use('*', cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['*'],
credentials: true,
}))
app.use('*', compress())
// Routes
app.route('/health', healthRoutes)
app.route('/api', apiRoutes)
// 404 handler
app.notFound((c) => c.json({ error: 'Not Found' }, 404))
// Error handler
app.onError((err, c) => {
console.error('Error:', err)
return c.json({ error: 'Internal Server Error' }, 500)
})
// Start server
const port = Number(process.env.PORT) || 8080
console.log(`🚀 Server starting on port ${port}`)
export default {
port,
fetch: app.fetch,
}3. src/routes/health.ts (Health Checks)
import { Hono } from 'hono'
const health = new Hono()
// Simple health check
health.get('/', (c) => {
return c.json({
status: 'ok',
timestamp: new Date().toISOString(),
service: 'my-new-service',
})
})
// Deep health check (with dependencies)
health.get('/ready', async (c) => {
const deep = c.req.query('deep') === 'true'
if (!deep) {
return c.json({ ok: true })
}
// Check dependencies
const checks = {
database: await checkDatabase(),
cache: await checkCache(),
}
const allHealthy = Object.values(checks).every(v => v === 'ok')
return c.json(
{
ok: allHealthy,
...checks,
timestamp: new Date().toISOString(),
},
allHealthy ? 200 : 503
)
})
// Liveness probe
health.get('/live', (c) => {
return c.json({ alive: true })
})
async function checkDatabase(): Promise<string> {
try {
// Add database ping here
return 'ok'
} catch (err) {
return 'error'
}
}
async function checkCache(): Promise<string> {
try {
// Add cache ping here
return 'ok'
} catch (err) {
return 'error'
}
}
export default health4. Dockerfile (Multi-stage Build)
# syntax=docker/dockerfile:1.7
# ============================================================================
# Multi-stage Dockerfile for My New Service
# Build from repository root: docker build -f services/my-new-service/Dockerfile .
# ============================================================================
# -------- Stage 1: base
FROM --platform=linux/amd64 oven/bun:1.3.6-debian AS base
WORKDIR /repo
ENV HUSKY=0
# -------- Stage 2: deps-dev (all dependencies)
FROM base AS deps-dev
ENV NODE_ENV=development HUSKY=0
# Copy workspace configuration
COPY bun.lock package.json ./
COPY packages ./packages
COPY services/my-new-service ./services/my-new-service
# Install dependencies
RUN --mount=type=cache,target=/root/.cache/bun \
HUSKY=0 bun install --ignore-scripts
# -------- Stage 3: deps-prod (production only)
FROM base AS deps-prod
ENV NODE_ENV=production HUSKY=0
COPY bun.lock package.json ./
COPY packages ./packages
COPY services/my-new-service/package.json ./services/my-new-service/
RUN --mount=type=cache,target=/root/.cache/bun \
HUSKY=0 bun install --production --ignore-scripts
# -------- Stage 4: prod (minimal runtime)
FROM --platform=linux/amd64 oven/bun:1.3.6-debian AS prod
WORKDIR /app
# Copy production dependencies
COPY --from=deps-prod /repo/node_modules ./node_modules
COPY --from=deps-prod /repo/services/my-new-service/node_modules ./services/my-new-service/node_modules
# Copy source code
COPY services/my-new-service ./services/my-new-service
# Non-root user
USER bun
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/usr/local/bin/bun", "run", "services/my-new-service/healthcheck.js"]
# Expose port
EXPOSE 8080
# Start service
CMD ["bun", "run", "services/my-new-service/src/index.ts"]5. cloudbuild.yaml (CI/CD)
# ============================================================================
# Cloud Build configuration for My New Service
# ============================================================================
steps:
# Build Docker image
- name: 'gcr.io/cloud-builders/docker'
env:
- 'DOCKER_BUILDKIT=1'
args:
- 'build'
- '--build-arg'
- 'BUILD_TIMESTAMP=${BUILD_ID}'
- '-f'
- 'services/my-new-service/Dockerfile'
- '-t'
- 'gcr.io/${PROJECT_ID}/my-new-service:${COMMIT_SHA}'
- '-t'
- 'gcr.io/${PROJECT_ID}/my-new-service:latest'
- '.'
timeout: 900s
# Push to Container Registry
- name: 'gcr.io/cloud-builders/docker'
args:
- 'push'
- '--all-tags'
- 'gcr.io/${PROJECT_ID}/my-new-service'
# Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
id: 'deploy-to-cloud-run'
args:
- 'run'
- 'deploy'
- '${_SERVICE_NAME}'
- '--image=gcr.io/${PROJECT_ID}/my-new-service:${COMMIT_SHA}'
- '--region=${_REGION}'
- '--platform=managed'
- '--allow-unauthenticated'
- '--quiet'
images:
- 'gcr.io/${PROJECT_ID}/my-new-service:${COMMIT_SHA}'
- 'gcr.io/${PROJECT_ID}/my-new-service:latest'
substitutions:
_SERVICE_NAME: my-new-service
_REGION: us-east1
timeout: 1800s6. .env.example
# Server
PORT=8080
NODE_ENV=development
# Database
DATABASE_URL=postgresql://localhost:5432/mydb
DATABASE_POOL_SIZE=10
# External Services
API_KEY=your-api-key-here
# Monitoring
LOG_LEVEL=info
# CORS
ALLOWED_ORIGINS=http://localhost:3000,https://example.com7. docker-compose.yml (Local Development)
version: '3.8'
services:
my-new-service:
build:
context: ../..
dockerfile: services/my-new-service/Dockerfile
ports:
- "8080:8080"
environment:
- PORT=8080
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
volumes:
- .:/repo/services/my-new-service
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:8. .dockerignore
node_modules
dist
.env
.env.*
!.env.example
*.log
.DS_Store
.git
.github
*.md
!README.md
coverage
.vscode
.idea9. tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["bun-types"],
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Configuration
Environment Variables
Required:
PORT- Server port (default: 8080)NODE_ENV- Environment (development/production)
Database (if needed):
DATABASE_URL- Connection stringDATABASE_POOL_SIZE- Connection pool size
Networking (if VPC needed):
- Configure VPC Access Connector in deployment
Secrets:
- Use Secret Manager for production
- Use environment variables for dev/staging
Best Practices
1. Structure
✅ Do:
- Group by feature (routes, middlewares, utils)
- Use TypeScript for type safety
- Follow consistent naming conventions
- Keep files small and focused
❌ Don't:
- Put everything in one file
- Mix concerns (business logic in routes)
- Skip error handling
- Hardcode configuration
2. Dependency Management
✅ Do:
- Use Bun workspaces for shared code
- Pin major versions in package.json
- Use monorepo
packages/for shared types - Install dependencies explicitly
❌ Don't:
- Use
npmoryarn(usebunonly) - Install global dependencies
- Copy-paste code between services
3. Docker
✅ Do:
- Use multi-stage builds
- Build from repository root
- Use .dockerignore
- Run as non-root user
- Add health checks
❌ Don't:
- Build from service directory
- Include dev dependencies in prod image
- Use latest tags in production
- Run as root
4. API Design
✅ Do:
- Use REST conventions
- Version your APIs (/api/)
- Add health endpoints
- Document with OpenAPI
- Use standard HTTP status codes
❌ Don't:
- Mix REST and RPC styles
- Skip versioning
- Return 200 for errors
- Ignore CORS configuration
5. Error Handling
✅ Do:
app.onError((err, c) => {
console.error('Error:', err)
if (err instanceof ValidationError) {
return c.json({ error: err.message }, 400)
}
if (err instanceof NotFoundError) {
return c.json({ error: 'Not found' }, 404)
}
return c.json({ error: 'Internal server error' }, 500)
})❌ Don't:
app.onError((err, c) => {
// Don't expose internal errors
return c.json({ error: err.stack }, 500)
})Deployment
First Deployment
# From repository root
cd /Users/vova/Code/CROP/microservices
# Build image
docker build -f services/my-new-service/Dockerfile -t gcr.io/noted-bliss-466410-q6/my-new-service:v1 .
# Push to registry
docker push gcr.io/noted-bliss-466410-q6/my-new-service:v1
# Deploy to Cloud Run
gcloud run deploy my-new-service \
--image gcr.io/noted-bliss-466410-q6/my-new-service:v1 \
--region us-east1 \
--project noted-bliss-466410-q6 \
--allow-unauthenticated \
--execution-environment gen2 \
--set-env-vars="$(cat services/my-new-service/.env.production | grep -v '^#' | xargs)"Subsequent Deployments
# Quick deploy
gcloud run deploy my-new-service \
--source services/my-new-service \
--region us-east1
# Or use Cloud Build
gcloud builds submit --config services/my-new-service/cloudbuild.yamlTesting
Local Testing
# Start service
cd services/my-new-service
bun run dev
# Test health endpoint
curl http://localhost:8080/health
# Run tests
bun testDocker Testing
# Build and run locally
docker-compose up
# Test
curl http://localhost:8080/healthChecklist
New Service Checklist
Setup
- Created service directory
- Initialized package.json
- Installed dependencies
- Created directory structure
Core Files
- src/index.ts (entry point)
- src/routes/health.ts (health checks)
- Dockerfile (multi-stage)
- docker-compose.yml (local dev)
- cloudbuild.yaml (CI/CD)
Configuration
- .env.example created
- .dockerignore configured
- tsconfig.json set up
- package.json scripts defined
Documentation
- README.md created
- CLAUDE.md added (detailed docs)
- API documented (OpenAPI)
- Environment variables documented
Testing
- Health endpoints tested
- Local Docker build tested
- Deployed to dev environment
- Integration tests passing
Deployment
- Image pushed to GCR
- Deployed to Cloud Run
- Environment variables configured
- VPC configured (if needed)
- Health checks passing
- Logs reviewed
Example: Complete New Service
See services/search/ for complete example with:
- OpenAPI documentation
- Comprehensive health checks
- VPC networking
- Database connections
- Monitoring endpoints
Next Steps
- Create service following this template
- Test locally with
bun run dev - Test Docker with
docker-compose up - Deploy to dev using
gcloud run deploy - Monitor logs and metrics
- Set up CI/CD (see
CI_CD_GUIDE.md) - Add to documentation (
ARCHITECTURE.md)
Need Help?
- Check
DEPLOYMENT_GUIDE.mdfor deployment issues - Check
services/search/for real-world example - Check
ARCHITECTURE.mdfor infrastructure details
Maintained by: Dev Team Last Review: 2025-11-19