| name | backend-bootstrapper |
| description | Bootstraps complete backend with Apso, including API setup, database configuration, and testing. Triggers when user needs to create backend, setup API, or initialize server. |
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
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
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?