Skip to Content
🚀 APSO is now in public beta. Get started →
Get StartedCore ConceptsServices & Schemas

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 EntityWhat Gets Generated
Definition in .apsorcTypeORM 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:

TypeDescriptionOptions
stringText dataminLength, maxLength, format, enum
integerWhole numbersmin, max
decimalDecimal numbersprecision, scale
booleanTrue/falsedefault
datetimeDate and timedefault (e.g., "now")
dateDate only
jsonJSON data
textLong text
uuidUUID stringdefault: "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

TypeDescriptionGenerated
hasManyOne-to-manyArray property
belongsToMany-to-oneForeign key
hasOneOne-to-oneSingle reference
belongsToManyMany-to-manyJunction 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 customer

Best Practices

Naming Conventions

  • Use PascalCase for entity names: Customer, OrderItem
  • Use camelCase for field names: signupDate, isActive
  • Use singular entity names: Customer not Customers
  1. Start simple — Define core entities first, add fields as needed
  2. Use appropriate types — decimal for money, datetime for timestamps
  3. Add indexes — Index fields you’ll filter or sort by frequently
  4. Define relationships — Let APSO generate the foreign keys

Next Steps

Last updated on