Relationships
Most mock servers hand you disconnected data. Akuma understands relationships, so a post's author is a real author that actually exists in the database.
Reference a schema as a type
Give a property the type of another schema and Akuma creates a relationship. It adds a foreign key column and seeds it with valid IDs from the referenced table.
schemas:
Author:
properties:
id: number
name: string
email: string
Post:
properties:
id: number
title: string
body: string
author: Author # creates authorId -> authors.id What Akuma does
- Foreign key column. The
authorproperty becomes anINTEGERcolumn namedauthorId(the property name in the configured style, camelCase by default). - References the right table.
authorIdpoints atauthors.id, the referenced schema's table. - Valid seed data. Every seeded post's
authorIdis a real author that exists in theauthorstable, never a random number. - Enforced. The column is created with a real
FOREIGN KEYconstraint, enforced by SQLite.
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
body TEXT,
authorId INTEGER,
FOREIGN KEY (authorId) REFERENCES authors(id)
); Foreign key naming
By default a foreign key is camelCase (authorId). Set naming.foreignKeys to match your house style. This affects only the generated FK columns; your other columns keep the names you write.
naming:
foreignKeys: snake_case # camelCase (default) | snake_case | PascalCase | Style | author: Author becomes |
|---|---|
camelCase (default) | authorId |
snake_case | author_id |
PascalCase | AuthorId |
Tip: tables are always snake_case (line_items), so snake_case foreign keys give a fully snake_case database.
Seeding order
Akuma seeds tables in dependency order: referenced tables first. Authors are created and seeded before posts, so by the time a post needs an authorId, real authors already exist to point at.
curl http://localhost:3000/posts
# => [{"id": 1, "title": "Example 1", "authorId": 2}, ...]
curl http://localhost:3000/authors/2
# => {"id": 2, "name": "Example 2", ...} <- authorId 2 is real A complete example
Authors, posts, and comments: two relationships, fully wired:
schemas:
Author:
properties:
id: number
name: string
Post:
properties:
id: number
title: string
author: Author # postId-side FK: authorId -> authors.id
Comment:
properties:
id: number
post: Post # creates postId -> posts.id
text: string
endpoints:
- path: /authors
method: GET
response:
type: array
schema: Author
- path: /posts
method: GET
response:
type: array
schema: Post
- path: /comments
method: GET
response:
type: array
schema: Comment Notes
- The foreign key column is named
<property>Id. A property already ending inIdis left as-is (noauthorIdId). - A relationship is detected when a property's type exactly matches a schema name. A plain
author: stringstays a plain text column. - Relationship cycles (A references B references A) are seeded best-effort, with some foreign keys left null; Akuma warns when it detects one.
See Data models for schema basics and Endpoints for serving them.
Common mistakes
A relationship column collides with a property
A relationship already adds an id column, so author: Author creates authorId. Declaring a separate authorId property collides, and Akuma refuses to start:
Error: Schema 'Post' produces a duplicate column 'authorId' from properties 'author' and 'authorId'. Relationship properties get an 'Id' suffix (e.g. `author` -> `authorId`), so rename one of them. A typo in the type silently becomes text
A relationship is detected only when the type exactly matches a schema name. A typo (author: Auther) is not a relationship; it becomes a plain text column, with a warning:
Schema 'Post' property 'author' has unknown type 'Auther'; treating it as text.