packages/serverpod/skills/serverpod-models/SKILL.md
Define Serverpod data models in YAML (.spy.yaml), serialization, database tables, relations, enums, and exceptions. Use when creating or editing models, database schema, .spy.yaml files, or Serverpod ORM entities.
npx skillsauth add serverpod/serverpod serverpod-modelsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Models are defined in .spy.yaml files anywhere under server lib/. They generate Dart classes for server and client, and optionally database tables.
After each change to models, ensure that the code is generated (automatically when a serverpod start is running or manually with serverpod generate). If models with table have changed, the database schema must be updated following the migration workflow.
class: Company
fields:
name: String
foundedDate: DateTime?
employees: List<Employee>
Field types: bool, int, double, String, Duration, DateTime, ByteData, UuidValue, Uri, BigInt, generated classes/enums/exceptions, List<T>, Map<K,V>, Set<T>, Record. Use ? for nullable.
class: Person
fields:
name: String
nickname: String?, required
Add table for a database table + ORM:
class: Company
table: company
fields:
name: String
foundedDate: DateTime?
serverOnly: truescope=serverOnly, scope=none (default all)!persist (not stored in DB)jsonKey=display_nameimmutable: true — final fields, ==, hashCode, copyWith.
extends: ParentClass — child inherits parent fields. Only one class in hierarchy has table.sealed: true — abstract sealed hierarchy for exhaustive subtypes. No table on sealed class.If parent is serverOnly, children must be too. Children cannot redefine parent fields.
enum: Status
values:
- pending
- active
- completed
Default serialization: byName. Set serialized: byIndex to use index.
Use exception: instead of class: for serializable exceptions. Same field types as classes; supports default and defaultModel. Uncaught exceptions become generic 500 errors on the client; only serializable exceptions send their data.
exception: MyException
fields:
message: String
errorType: MyEnum
Throw on server, catch on client:
// Server
throw MyException(message: 'Failed', errorType: MyEnum.thingyError);
// Client
try {
await client.example.doThingy();
} on MyException catch (e) {
print(e.message);
}
Serializable exceptions can also be sent over streams (both directions; the stream closes after). Do not put sensitive data in exception fields — they are sent to the client.
indexes:
company_name_idx:
fields: name
unique: true
One-to-one (FK with unique index):
addressId: int, relation(parent=address) + unique index on addressIdaddress: Address?, relation (generates addressId)relation(optional) for nullable FK; relation(field=customId) for custom FK namerelation(name=...) on both sides, field= on FK sideOne-to-many:
employees: List<Employee>?, relationcompanyId: int, relation(parent=company) (no unique index)relation(name=company_employees) on both sidesMany-to-many: Use a join table model with two relation fields.
Querying: include for eager loading, includeList with where/orderBy/limit/offset for list relations. attach/detach for managing relations.
Models with the table keyword can also generate a client-side database with the database keyword:
class: Company
table: company
database: client
| Value | Description |
| ------- | ----------- |
| server | Generates tables only on the server, and a non-table model on the client package (default). |
| client | Generates tables only on the client, and a non-table model on the server package. |
| all | Generates table models on both server and client. |
For how to use the client-side database, see the Serverpod Database skill.
To keep backward compatibility, do not change or remove fields in serialized classes used by clients. Add new fields only if nullable or with a default value, so older clients that don't send the field still work.
To use serializable models not in YAML: implement toJson(), fromJson, copyWith(). Register in config/generator.yaml under extraClasses with full URI (e.g. package:my_shared/my_shared.dart:ClassName). Both server and client must depend on the package. Freezed classes with fromJson work the same way. Implement ProtocolSerialization with toJsonForProtocol() to omit fields when sending to client.
development
Build highly distinctive, production-ready Flutter interfaces with exceptional design fidelity. Include this skill whenever a user requests Flutter widgets, screens, or full apps.
testing
Serverpod Authentication — Signing in users, verify if they are authenticated, assinging scopes (e.g., admin). Use when adding features that require the user to be signed in.
development
Serverpod web server (Relic) — REST APIs, webhooks, middleware, static files, server-rendered HTML, SPAs, Flutter web. Use when adding HTTP routes, serving web pages or web apps, intercepting requests, or working with the Relic web server.
tools
Serverpod overview — what it is, project structure, how to work with. Always use at least once when working with projects that use Serverpod.