Create Frontend-Callable Backend Service
Use this guide when creating a new backend service that needs to be accessible by the frontend (publicly exposed via Load Balancer).
Part 1: Implement the Service (alphacode repository)
1. Create the Service Directory Structure
Location: services/<service_name>/
Create the following folder structure:
services/<service_name>/
├── prisma/
│ └── <service_name>/
│ ├── migrations/
│ ├── schema.prisma
│ ├── prisma.config.ts
│ ├── scripts/
│ │ └── index.ts
│ └── seed/
│ └── seed.ts
├── src/
│ ├── app.module.ts
│ ├── main.ts
│ ├── metadata.ts
│ ├── config/
│ │ └── app.config.ts
│ ├── application-layer/
│ │ └── index.ts
│ ├── healthcheck/
│ │ ├── healthcheck.controller.ts
│ │ ├── healthcheck.module.ts
│ │ └── services/
│ │ ├── <service_name>-prisma-health.service.ts
│ │ └── index.ts
│ ├── infrastructure-layer/
│ │ ├── api/
│ │ │ ├── auth/
│ │ │ │ ├── auth.module.ts
│ │ │ │ └── index.ts
│ │ │ ├── context/
│ │ │ │ ├── context.module.ts
│ │ │ │ ├── context.service.ts
│ │ │ │ └── index.ts
│ │ │ ├── controllers/
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── persistence/
│ │ │ ├── prisma/
│ │ │ │ ├── client/
│ │ │ │ │ ├── <service_name>/
│ │ │ │ │ │ ├── <service_name>.prisma.module.ts
│ │ │ │ │ │ ├── <service_name>.prisma.service.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── repositories/
│ │ │ │ │ └── index.ts
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ └── generated/
│ └── swagger/
│ └── <service_name>.swagger.json
├── Dockerfile
├── entrypoint.sh
├── package.json
├── project.json
├── nest-cli.json
├── eslint.config.mjs
├── vitest.config.ts
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.build.json
└── tsconfig.spec.json2. Configure the Service Entry Point
src/main.ts:
import { bootstrapNestApp, initTracer } from '@package/nest';
initTracer();
import { AppModule } from './app.module';
const APP_NAME = '<service_name>';
async function bootstrap() {
await bootstrapNestApp({
appModule: AppModule,
appName: APP_NAME,
isQueryExtended: true,
hasFrontendClient: true,
});
}
bootstrap();3. Configure the App Module
src/app.module.ts:
Import and configure the required modules:
ConfigModulewith environment validationContextModulefor request context (CLS)LoggingModulefor structured loggingHealthcheckModulefor ALB health checksAuthModulefor Supertokens authenticationAnalyticModulefor PostHog analytics- Database module (e.g.,
<ServiceName>PrismaModule)
4. Configure Environment Validation
src/config/app.config.ts:
Define a class with class-validator decorators for all required environment variables:
- Database:
DATABASE_USERNAME,DATABASE_PASSWORD,DATABASE_ADDRESS,DATABASE_PORT,DATABASE_NAME - Server:
HOST,PORT,FRONTEND_CLIENT_HOST - Redis:
REDIS_HOST,REDIS_PORT,REDIS_STREAM_CONSUMER_GROUP - Supertokens:
SUPER_TOKEN_CORE_API_KEY,SUPER_TOKEN_CONNECTION_URI,SUPER_TOKEN_GOOGLE_OAUTH_CLIENT_ID, etc. - Logging:
LOGGING_APP_NAME,LOGGING_LEVEL,LOGGING_IS_USING_JSON - Analytics:
POSTHOG_API_KEY,POSTHOG_HOST
5. Set Up Prisma
prisma/<service_name>/schema.prisma:
generator client {
provider = "prisma-client-js"
output = "../../../node_modules/@prisma/<service_name>-db-client"
}
datasource db {
provider = "postgresql"
}
// Define your models hereprisma/<service_name>/prisma.config.ts:
import path from 'node:path';
import { defineConfig, env } from 'prisma/config';
type Env = {
DATABASE_URL: string;
};
export default defineConfig({
schema: path.join(__dirname, 'schema.prisma'),
datasource: {
url: env<Env>('DATABASE_URL'),
},
migrations: {
path: path.join(__dirname, 'migrations'),
seed: 'tsx prisma/<service_name>/seed/seed.ts',
},
});6. Configure NX Project
project.json:
Include the following targets:
clean: Remove generated metadatatest: Vitest configurationprisma:generate:<service_name>: Generate Prisma clientclient:export:nest: Export Swagger client for@package/nestclient:export:frontend: Export Swagger client for frontendbuild: Build with NestJS CLIdev: Development mode with watch
7. Create Dockerfile
FROM node:22
RUN apt-get update && apt-get install -y \
openssl curl nodejs npm graphicsmagick ghostscript \
&& rm -rf /var/lib/apt/lists/*
RUN npm i -g corepack@latest && corepack enable
RUN npm install -g nx
WORKDIR /app
COPY nx.json package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.base.json ./
COPY packages packages
COPY services/<service_name> services/<service_name>
RUN pnpm install --ignore-scripts --frozen-lockfile --strict-peer-dependencies=false
ARG GITHUB_TOKEN
ENV GITHUB_TOKEN=${GITHUB_TOKEN:-}
RUN nx build <service_name> --skip-nx-cache
EXPOSE <port>
RUN chmod +x /app/services/<service_name>/entrypoint.sh
ENTRYPOINT ["/app/services/<service_name>/entrypoint.sh"]8. Create Entrypoint Script
entrypoint.sh:
#!/bin/sh
urlencode() {
echo "$1" | sed 's/:/%3A/g; s/@/%40/g; ...'
}
ENCODED_PASSWORD=$(urlencode "$DATABASE_PASSWORD")
export DATABASE_URL="postgresql://${DATABASE_USERNAME}:${ENCODED_PASSWORD}@${DATABASE_ADDRESS}:${DATABASE_PORT}/${DATABASE_NAME}?schema=public"
echo "🚀 Running Prisma migrations..."
pnpm --filter <service_name> run migrate:deploy:all
echo "🔄 Running scripts on database..."
pnpm --filter <service_name> run prepare:migration
echo "✅ Prisma migrations applied. Starting the app..."
exec node services/<service_name>/dist/mainPart 2: Implement and Deploy the Infrastructure
/infra repository
1. ECR Repository (Global/Tooling)
Location: aws-iac/parallel_tooling/<region>/shared/containers/ecr/<service_name>/terragrunt.hcl
- Create the ECR repository definition.
- Grant read/write access to sandbox, staging, and prod accounts.
2. Database (Optional)
Location: aws-iac/parallel_<env>/<region>/<env>/database/<service_name>/
- db-security-group: Create security group allowing ingress from the service's security group.
- db: Create the RDS instance configuration.
instance_class: Typicallydb.t4g.microfor staging.snapshot_identifier(Optional): Only use this if you need to restore the database from an existing snapshot (e.g.service-kms). Do not set this for a new, empty database.
3. Service Infrastructure
Location: aws-iac/parallel_<env>/<region>/<env>/containers/services/<service_name>/ Create the following structure:
service.hcl: Define localservice = "<service_name>".service-security-group/: Security group for the ECS service.service-task-exec-role-policy/: IAM policy for task execution (ECR, Secrets).service-task-role-policy/: IAM policy for the running task (Logs, specialized access).service/: Main ECS service definition.- Dependencies:
vpc,alb,db(if exists),redis(if exists),s3(if needed for sampledata/docs). - Environment Variables:
HOST,PORT,DATABASE_...,REDIS_...,S3_...,FRONTEND_CLIENT_HOST. - Secrets:
DATABASE_PASSWORD,DD_API_KEY, etc. - Resources: Adjust CPU/Memory based on usage (e.g.
256/512for read-heavy/light services).
- Dependencies:
4. Redis Access (Optional)
Location: aws-iac/parallel_<env>/<region>/<env>/cache/redis/redis-security-group/terragrunt.hcl
- Add an ingress rule allowing TCP traffic from
<service_name>_service_security_group.
5. Load Balancing (ALB)
Location: aws-iac/parallel_<env>/<region>/<env>/load-balancing/alb/terragrunt.hcl
- Target Group: Add a new target group key for the service (HTTP, port 80, healthcheck).
- Listener Rule: Add a listener rule (HTTPS/443).
- Priority: Ensure unique priority.
- Condition: Host header
<service_name>.<hosted_zone_name>. - Action: Forward to the new target group.
Location: aws-iac/parallel_<env>/<region>/<env>/load-balancing/alb-additional-certificates/terragrunt.hcl
- Certificate: Add a new certificate entry for
<service_name>.<hosted_zone_name>.
6. DNS Records
Location: aws-iac/parallel_<env>/us-east-1/<env>/route53/records/terragrunt.hcl
- Add an
Arecord.- Name:
<service_name>.${dependency.main_dns_zone.outputs.name} - Alias: Point to the ALB DNS name and Zone ID.
- Name:
7. Logging (Datadog)
Location: aws-iac/parallel_<env>/<region>/<env>/containers/datadog-cloudwatch-log-subscription-filters/terragrunt.hcl
- Dependency: Add a dependency block for the new service.
- Filter: Add a new entry in
log_subscription_filterspointing to the service's CloudWatch log group.
8. CI/CD (GitHub)
Location: aws-iac/parallel_<env>/<region>/<env>/github/oidc/terragrunt.hcl
- Ensure the GitHub repository is listed in allowed repositories for OIDC.