Backend Bootstrapper
I set up production-ready backends using Apso, giving you a fully functional REST API in minutes.
What I Create
1. Apso Service Configuration
Complete .apsorc schema file with:
- •All entities defined
- •Relationships configured
- •Validation rules
- •Indexes optimized
- •Multi-tenancy enabled
2. Generated NestJS Backend
Apso auto-generates:
- •REST API with OpenAPI docs
- •CRUD endpoints for all entities
- •TypeORM models
- •Database migrations
- •Validation middleware
- •Error handling
- •Logging
3. Database Setup
- •PostgreSQL database (local or AWS RDS)
- •All tables created
- •Relationships enforced
- •Migrations ready
- •Seed data (optional)
4. Development Environment
- •Docker Compose for local database
- •Environment variable configuration
- •Development server running
- •Hot reload enabled
5. API Documentation
- •OpenAPI/Swagger documentation
- •Interactive API testing UI
- •Type definitions exported
- •Example requests
The Bootstrap Process
Step 1: Validate Schema
I'll review the schema from schema-architect and:
- •Check for missing fields
- •Validate relationships
- •Ensure multi-tenancy
- •Add recommended indexes
- •Suggest optimizations
Step 2: Create Apso Project
# Install Apso CLI npm install -g @apso/apso-cli # Create new service apso server new --name your-service-backend # Navigate to project cd your-service-backend
Step 3: Configure Schema
I'll create the .apsorc file with your entities:
{
"service": "your-service-api",
"version": "1.0.0",
"database": {
"provider": "postgresql",
"host": "localhost",
"port": 5432,
"database": "your_service_db"
},
"auth": {
"enabled": true,
"provider": "better-auth"
},
"multiTenant": true,
"entities": {
// Your schema here
}
}
Step 4: Generate Code
# Generate NestJS backend apso server scaffold # This creates: # src/ # autogen/ ← Generated code (DON'T EDIT) # extensions/ ← Your custom code # common/ ← Shared utilities # main.ts ← Entry point
Step 5: Install Dependencies
npm install
Step 6: Start Database
# Start PostgreSQL via Docker npm run compose # This starts: # - PostgreSQL on port 5432 # - pgAdmin on port 5050 (optional)
Step 7: Provision Database
# Create tables and run migrations npm run provision # This: # - Creates all tables # - Sets up foreign keys # - Creates indexes # - Runs seed data (if any)
Step 8: Start Development Server
# Start backend server npm run start:dev # Server runs at: # - API: http://localhost:3001 # - OpenAPI Docs: http://localhost:3001/api/docs # - Health Check: http://localhost:3001/health
Step 9: Verify & Test
I'll test all endpoints:
# Health check curl http://localhost:3001/health # Test CRUD endpoints curl http://localhost:3001/organizations curl http://localhost:3001/users curl http://localhost:3001/projects
Generated API Structure
For each entity, you get:
Standard REST Endpoints
List
GET /entities
Query params: ?page=1&limit=10&sort=created_at&order=desc
Response: { data: [...], total: 100, page: 1, limit: 10 }
Get by ID
GET /entities/:id
Response: { id, ...fields }
Create
POST /entities
Body: { field1: value1, field2: value2 }
Response: { id, ...fields, created_at, updated_at }
Update
PUT /entities/:id
PATCH /entities/:id (partial update)
Body: { field1: newValue }
Response: { id, ...fields, updated_at }
Delete
DELETE /entities/:id
Response: { success: true }
Filtering & Querying
Filter by field
GET /entities?status=active GET /entities?created_at_gte=2024-01-01
Full-text search
GET /entities?search=keyword
Relations
GET /entities?include=relations GET /organizations/123/users (nested route)
Aggregations
GET /entities/count GET /entities/stats
Automatic Features
1. Multi-Tenancy
Every request is automatically scoped to the organization:
// Middleware adds organization context
@UseGuards(OrgGuard)
export class ProjectController {
// All queries filtered by req.organizationId
async findAll(@Req() req) {
// Only returns projects for req.organizationId
}
}
2. Validation
Input validation with class-validator:
// Automatically validated
class CreateProjectDto {
@IsString()
@MinLength(3)
@MaxLength(100)
name: string;
@IsEnum(['active', 'archived'])
status: string;
}
3. Error Handling
Consistent error responses:
{
"statusCode": 400,
"message": "Validation failed",
"errors": [
{
"field": "name",
"message": "name must be at least 3 characters"
}
]
}
4. Logging
Structured logging with Winston:
// Automatic logging of: // - All requests // - Errors // - Database queries // - Performance metrics
5. OpenAPI Documentation
Interactive docs at /api/docs:
- •All endpoints documented
- •Request/response schemas
- •Try-it-out functionality
- •Example requests
File Structure
your-service-backend/ ├── src/ │ ├── autogen/ ← ⚠️ NEVER MODIFY - Generated by Apso │ │ ├── Organization/ ← Entity-specific modules │ │ │ ├── Organization.entity.ts │ │ │ ├── Organization.service.ts │ │ │ ├── Organization.controller.ts │ │ │ └── Organization.module.ts │ │ ├── User/ │ │ ├── guards/ ← ⚠️ AUTO-GENERATED - Auth & scope guards │ │ │ ├── auth.guard.ts # AuthGuard for session validation │ │ │ ├── scope.guard.ts # ScopeGuard for multi-tenant data isolation │ │ │ ├── guards.module.ts # NestJS module for guards │ │ │ └── index.ts # Barrel exports │ │ └── index.ts │ │ │ ├── extensions/ ← ✅ YOUR CUSTOM CODE (safe to modify) │ │ ├── Organization/ │ │ │ ├── Organization.controller.ts (add custom endpoints) │ │ │ └── Organization.service.ts (add business logic) │ │ ├── User/ │ │ ├── Project/ │ │ └── auth/ (Better Auth integration) │ │ │ ├── common/ ← Shared utilities │ │ ├── interceptors/ │ │ ├── decorators/ │ │ └── filters/ │ │ │ └── main.ts ← App entry point │ ├── test/ ← Tests │ ├── unit/ │ └── e2e/ │ ├── .apsorc ← Schema definition ├── docker-compose.yml ← Local database ├── package.json └── README.md
Important: Guards are now generated inside src/autogen/guards/ to clearly indicate they are auto-generated. All files in autogen/ are overwritten on every apso server scaffold run.
Customization Options
Adding Custom Endpoints
// src/extensions/Project/Project.controller.ts
import { Controller, Post, Param } from '@nestjs/common';
@Controller('projects')
export class ProjectController {
// Add custom endpoint
@Post(':id/archive')
async archive(@Param('id') id: string) {
// Your custom logic
return this.projectService.archive(id);
}
@Get(':id/statistics')
async getStats(@Param('id') id: string) {
// Custom aggregation
return this.projectService.getStatistics(id);
}
}
Adding Business Logic
// src/extensions/Project/Project.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProjectService {
async archive(id: string) {
// Complex business logic
const project = await this.findOne(id);
// Archive all tasks
await this.taskService.archiveByProject(id);
// Update project status
return this.update(id, { status: 'archived' });
}
}
Adding Middleware
// src/common/interceptors/logging.interceptor.ts
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const req = context.switchToHttp().getRequest();
const { method, url } = req;
console.log(`${method} ${url}`);
return next.handle();
}
}
Environment Configuration
I'll create .env files:
# .env (local development) NODE_ENV=development PORT=3001 # Database DB_HOST=localhost DB_PORT=5432 DB_NAME=your_service_db DB_USER=postgres DB_PASSWORD=postgres # Better Auth AUTH_SECRET=your-secret-key-here AUTH_URL=http://localhost:3001 # AWS (for production) AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= # Stripe STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_...
Testing
I'll set up testing structure:
// test/e2e/project.e2e-spec.ts
describe('ProjectController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/projects (GET)', () => {
return request(app.getHttpServer())
.get('/projects')
.expect(200)
.expect((res) => {
expect(res.body.data).toBeInstanceOf(Array);
});
});
it('/projects (POST)', () => {
return request(app.getHttpServer())
.post('/projects')
.send({ name: 'Test Project', organization_id: '...' })
.expect(201)
.expect((res) => {
expect(res.body.name).toBe('Test Project');
});
});
});
Verification Checklist
Before marking bootstrap as complete, I verify:
- •✅ Server starts without errors
- •✅ Database connection successful
- •✅ All tables created
- •✅ OpenAPI docs accessible
- •✅ CRUD endpoints work for all entities
- •✅ Multi-tenancy filtering active
- •✅ Validation works on create/update
- •✅ Error handling returns proper responses
- •✅ Environment variables configured
- •✅ Docker Compose running
Critical Configuration Rules
IMPORTANT: Before running any Apso commands, review the learnings document at .devenv/docs/reference/APSO-SETUP-LEARNINGS.md for verified fixes.
Key Rules Summary
- •
.apsorcrootFolder must be./src- Notsrc, not./src/generated, exactly./src - •Docker compose scripts must use env vars - Don't append
:5432to DATABASE_PORT - •Provision scripts must use env credentials - Use
${DATABASE_USERNAME}not hardcodedroot - •Quote values with spaces in
.env-APP_NAME="GoLotus API"notAPP_NAME=GoLotus API - •Remove
versionfrom docker-compose.yml - It's deprecated in Docker Compose v2
Verification Checklist
# After setup, these should all work: npm run compose # PostgreSQL container starts npm run provision # Tables created successfully npm run start:dev # Server on expected port curl http://localhost:3100/health # "I am up!"
Common Issues & Solutions
Issue: "Cannot connect to database"
Fix: Ensure npm run compose is running and ports aren't conflicting
Issue: "Module not found"
Fix: Run npm install after generating code
Issue: "TypeORM entity not found"
Fix: Run npm run provision to sync schema
Issue: "Port 3001 already in use"
Fix: Kill existing process: lsof -ti:3001 | xargs kill
Issue: "Invalid IP address" during compose
Fix: Check apso-compose.sh isn't appending :5432 to DATABASE_PORT
Issue: "FATAL: password authentication failed for user 'root'"
Fix: Provision scripts should use ${DATABASE_USERNAME} and ${DATABASE_PASSWORD} from env
Issue: "API: command not found" when loading .env
Fix: Quote values with spaces: APP_NAME="My API" not APP_NAME=My API
What's Next?
After bootstrap, you're ready for:
- •Frontend Setup - Call
frontend-bootstrapper - •Authentication - Call
auth-implementer - •Custom Endpoints - Add business logic to extensions/
- •Testing - Call
test-generator
Ready?
I'll bootstrap a production-ready backend in about 5 minutes. Just provide your schema (or I can call schema-architect first if you don't have one yet).
Do you have a schema ready, or should I design one first?