Skip to Content
🚀 APSO is now in public beta. Get started →
GuidesSchemaOverview

Schema Design

Apso uses a declarative schema system to define your data model. You describe your entities, fields, and relationships in a single .apsorc configuration file, and the Apso CLI generates a complete, production-ready NestJS backend — TypeORM entities, CRUD controllers, services, DTOs, validation, and database migrations.

The .apsorc File

Your schema lives in a JSON file called .apsorc at the root of your service project. Every .apsorc file follows the version 2 format with four top-level properties:

.apsorc
{ "version": 2, "rootFolder": "src", "entities": [ { "name": "Project", "created_at": true, "updated_at": true, "fields": [ { "name": "name", "type": "text" }, { "name": "description", "type": "text", "nullable": true }, { "name": "status", "type": "enum", "values": ["Active", "Archived"], "default": "Active" } ] } ], "relationships": [ { "from": "Project", "to": "Workspace", "type": "ManyToOne" } ] }
PropertyTypeRequiredDescription
version2YesMust always be 2. Identifies the schema format version.
rootFolderstringYesDirectory where generated code is placed (typically "src").
entitiesarrayYesArray of entity definitions. Each entity becomes a database table and API resource.
relationshipsarrayYesArray of relationship definitions connecting entities.

Schema Components

Schema Design Principles

1. Start simple, iterate

Begin with the minimum viable schema. You can add fields, entities, and relationships later — re-run apso server scaffold and your code regenerates. Start with two or three core entities and expand from there.

2. Use meaningful names

Entity and field names flow directly into your database columns, API endpoints, and TypeScript types. Choose names that are:

  • PascalCase for entities: ProjectMember, not project_member or PM
  • Singular: Project, not Projects
  • Descriptive: ApplicationServiceMetric, not ASM

3. Define relationships once

This is the single most important rule in Apso schema design. Only define one side of each relationship. Apso auto-generates the inverse side. Defining both sides causes duplicate properties, TypeScript compilation errors, and entity conflicts. See Relationships for details.

4. Plan for multi-tenancy early

If you are building a SaaS application, add scopeBy to your tenant-scoped entities from the start. Retrofitting data isolation later is significantly harder than designing for it upfront. See Multi-Tenancy.

What Gets Generated

From your .apsorc file, running apso server scaffold generates:

ComponentLocationDescription
TypeORM Entitiessrc/autogen/{Entity}/Type-safe entity classes with decorators, validation, and relationships
Controllerssrc/autogen/{Entity}/REST endpoints with full CRUD (GET, POST, PATCH, DELETE)
Servicessrc/autogen/{Entity}/Business logic layer wrapping TypeORM repositories
DTOssrc/autogen/{Entity}/Create and Update data transfer objects with class-validator decorators
Modulessrc/autogen/{Entity}/NestJS modules wiring controllers, services, and entity repositories
Enumssrc/autogen/enums.tsTypeScript enums for all enum-typed fields
Auth Guardssrc/guards/Authentication and scope guards (when auth / scopeBy configured)

Never modify files in src/autogen/. They are regenerated every time you scaffold. Place all custom business logic in src/extensions/{Entity}/ instead. See Extending Generated Code for the pattern.

Workflow

The typical development workflow with Apso:

# 1. Create a new project apso server new --name my-api # 2. Edit .apsorc to define your schema # 3. Generate code apso server scaffold # 4. Start the database npm run compose # 5. Provision the schema npm run provision # 6. Start the dev server npm run start:dev

When you change your schema, repeat steps 2-3. For rapid prototyping, enable DATABASE_SYNC=true in your .env to skip manual migrations.

Next Steps

Last updated on