Services & Schemas
Services and schemas are the foundation of every APSO backend.
What is a Service?
A service is a complete backend application with its own:
- Database
- API endpoints
- Authentication
- Multi-tenant data isolation
Services are defined in a .apsorc file at your project root.
.apsorc
{
"service": {
"name": "my-saas-api",
"description": "Backend for My SaaS Product"
}
}What is an Entity?
An entity represents a data model in your application. Each entity becomes:
| APSO Entity | What Gets Generated |
|---|---|
Definition in .apsorc | TypeORM entity class |
| Database table | |
| NestJS controller | |
| CRUD service | |
| DTOs (Create, Update, Response) | |
| API endpoints |
Defining Entities
Entities are defined in the entities section of your .apsorc:
.apsorc
{
"entities": {
"Customer": {
"fields": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "format": "email", "unique": true },
"status": {
"type": "string",
"enum": ["active", "inactive", "churned"],
"default": "active"
},
"signupDate": { "type": "datetime" },
"revenue": { "type": "decimal", "precision": 10, "scale": 2 }
}
}
}
}Field Types
APSO supports these field types:
| Type | Description | Options |
|---|---|---|
string | Text data | minLength, maxLength, format, enum |
integer | Whole numbers | min, max |
decimal | Decimal numbers | precision, scale |
boolean | True/false | default |
datetime | Date and time | default (e.g., "now") |
date | Date only | |
json | JSON data | |
text | Long text | |
uuid | UUID string | default: "uuid" |
Field Options
Common options for all fields:
{
"fieldName": {
"type": "string",
"required": true, // Validation: must be provided
"unique": true, // Database: unique constraint
"index": true, // Database: add index
"default": "value", // Default value
"nullable": false // Database: NOT NULL
}
}Relationships
Define relationships between entities:
.apsorc
{
"entities": {
"Customer": {
"fields": { "name": { "type": "string" } },
"relationships": {
"orders": { "type": "hasMany", "entity": "Order" }
}
},
"Order": {
"fields": {
"total": { "type": "decimal" },
"status": { "type": "string" }
},
"relationships": {
"customer": { "type": "belongsTo", "entity": "Customer" }
}
}
}
}Relationship Types
| Type | Description | Generated |
|---|---|---|
hasMany | One-to-many | Array property |
belongsTo | Many-to-one | Foreign key |
hasOne | One-to-one | Single reference |
belongsToMany | Many-to-many | Junction table |
Generated Output
For each entity, APSO generates:
Entity Class
src/entities/customer.entity.ts
@Entity('customers')
export class Customer {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column({ unique: true })
email: string;
@Column({ default: 'active' })
status: 'active' | 'inactive' | 'churned';
@OneToMany(() => Order, order => order.customer)
orders: Order[];
}API Endpoints
GET /customers List all customers
POST /customers Create a customer
GET /customers/:id Get a customer
PATCH /customers/:id Update a customer
DELETE /customers/:id Delete a customerBest Practices
Naming Conventions
- Use PascalCase for entity names:
Customer,OrderItem - Use camelCase for field names:
signupDate,isActive - Use singular entity names:
CustomernotCustomers
- Start simple — Define core entities first, add fields as needed
- Use appropriate types —
decimalfor money,datetimefor timestamps - Add indexes — Index fields you’ll filter or sort by frequently
- Define relationships — Let APSO generate the foreign keys
Next Steps
Last updated on