Resource Schema Structure
kind: <string> # Required
apiVersion: <string> # Required
metadata: # Optional
description: <string>
version: <string>
version_in_path: <boolean>
methods: # Required
resource: [<http-methods>]
instance: [<http-methods>]
descriptions: # Optional
resource: {<method>: <string>}
instance: {<method>: <string>}
schema: # Required
type: array # Must be "array"
key: # Required for instance methods
name: <string>
description: <string>
schema: <json-schema>
query_params: [...] # Optional
items: # Required
type: object # Usually "object"
properties: {...}
required: [...]
default_query_params: [...] # Optional
security: # Optional
scheme: {...}
resource: [<methods>]
instance: [<methods>]
asyncapi: # Optional
publish: <boolean>
subscribe: <boolean>
validations: # Optional
rules: [...]
Top-Level Fields
kind (Required)
Type: String
Description: Resource name used in API paths. By convention this should be the plural noun — it is used directly as the URL path segment.
URL Format: /{kind} and /{kind}/{id}
Rules:
- ✅ Lowercase
- ✅ Plural nouns recommended
- ✅ Alphanumeric + hyphens/underscores
- ❌ No spaces or special characters
Examples:
kind: users # → /users, /users/{user_id}
kind: blog-posts # → /blog-posts, /blog-posts/{post_id}
kind: product_categories # → /product_categories, /product_categories/{category_id}
plural (Optional)
Type: String
Description: Explicit plural form of the resource kind, used as the URL path when kind cannot be the plural noun itself (e.g. a composite word whose plural Firestone cannot infer).
Example:
kind: subclass
plural: subclasses # → /subclasses, /subclasses/{id}
When omitted, kind is used as the URL path verbatim.
singular (Optional)
Type: String
Description: Explicit singular form of the resource kind, used when deriving model type names (e.g. CreateProxy). Only needed when the automatic singularization produces the wrong result.
Example:
kind: proxies
singular: proxy # model types: Proxy, CreateProxy, UpdateProxy
When omitted, the singular is inferred from kind (or plural) automatically.
apiVersion (Required)
Type: String
Description: Resource definition version
Current: Always "v1"
apiVersion: v1 # Required, always "v1" currently
metadata (Optional)
Type: Object Description: Resource-level metadata
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
description | string | No | Human-readable description |
version | string | No | Semantic version (e.g., "1.0.0") |
version_in_path | boolean | No | Include version in URL path |
Example:
metadata:
description: User account management
version: "2.1.0"
version_in_path: false # → /users (not /v2.1.0/users)
methods (Required)
Type: Object Description: HTTP methods to expose
Structure:
methods:
resource: [<methods>] # Collection endpoint: /{kind}
instance: [<methods>] # Instance endpoint: /{kind}/{id}
Valid HTTP Methods:
get- Retrieve resourcespost- Create resourceput- Replace resourcepatch- Update resourcedelete- Delete resource
Examples:
# Full CRUD
methods:
resource: [get, post]
instance: [get, put, delete]
# Read-only
methods:
resource: [get]
instance: [get]
# Create-only
methods:
resource: [post]
instance: []
# Custom operations
methods:
resource: [get, post]
instance: [get, patch] # PATCH instead of PUT
descriptions (Optional)
Type: Object Description: Human-readable operation descriptions
Structure:
descriptions:
resource:
get: "Description for GET /{kind}"
post: "Description for POST /{kind}"
instance:
get: "Description for GET /{kind}/{id}"
put: "Description for PUT /{kind}/{id}"
delete: "Description for DELETE /{kind}/{id}"
Example:
descriptions:
resource:
get: List all users with optional filtering
post: Create a new user account
instance:
get: Retrieve detailed user information
put: Update user account
delete: Permanently delete user account
schema (Required)
Type: Object Description: JSON Schema definition for the resource
schema.type (Required)
Must be: "array"
schema:
type: array # Always "array"
schema.key (Required for instance methods)
Type: Object Description: Identifier field for instance endpoints
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✅ Yes | Key field name |
description | string | No | Key description |
schema | object | ✅ Yes | JSON Schema for key |
Examples:
# UUID key
key:
name: user_id
description: Unique user identifier
schema:
type: string
format: uuid
# Integer key
key:
name: id
schema:
type: integer
minimum: 1
# String slug
key:
name: slug
schema:
type: string
pattern: '^[a-z0-9-]+$'
minLength: 3
maxLength: 50
schema.items (Required)
Type: Object Description: JSON Schema for resource objects
Common Structure:
items:
type: object
properties:
field_name:
type: <type>
description: <description>
# ... validation rules
required: [list, of, required, fields]
Example:
items:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
description: User's full name
email:
type: string
format: email
maxLength: 254
description: Email address
age:
type: integer
minimum: 0
maximum: 150
description: User's age
is_active:
type: boolean
default: true
description: Account status
required: [name, email]
schema.query_params (Optional)
Type: Array of objects Description: Query parameters for filtering/pagination
Structure:
query_params:
- name: <param_name>
description: <description>
required: <boolean>
schema: <json-schema>
methods: [<http-methods>]
Example:
query_params:
- name: status
description: Filter by status
required: false
schema:
type: string
enum: [active, inactive, pending]
methods: [get]
- name: limit
description: Number of results
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
methods: [get]
- name: offset
description: Pagination offset
required: false
schema:
type: integer
minimum: 0
default: 0
methods: [get]
default_query_params (Optional)
Type: Array of objects Description: Query parameters added to all GET requests
Example:
default_query_params:
- name: limit
schema:
type: integer
default: 20
- name: offset
schema:
type: integer
default: 0
security (Optional)
Type: Object Description: Security scheme definitions and requirements
Structure:
security:
scheme:
<scheme_name>:
type: <type>
# ... scheme-specific fields
resource: [<methods>] # Methods requiring auth on /{kind}
instance: [<methods>] # Methods requiring auth on /{kind}/{id}
Security Scheme Types:
HTTP Bearer (JWT)
security:
scheme:
bearer_auth:
type: http
scheme: bearer
bearerFormat: JWT
resource: [post]
instance: [put, delete]
API Key
security:
scheme:
api_key:
type: apiKey
in: header
name: X-API-Key
resource: [get, post]
instance: [get, put, delete]
OAuth2
security:
scheme:
oauth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/oauth/authorize
tokenUrl: https://auth.example.com/oauth/token
scopes:
read: Read access
write: Write access
resource: [get, post]
instance: [get, put, delete]
Multiple Schemes
security:
scheme:
bearer_auth:
type: http
scheme: bearer
api_key:
type: apiKey
in: header
name: X-API-Key
resource: [post] # Either bearer OR api_key required
instance: [put, delete]
asyncapi (Optional)
Type: Object Description: AsyncAPI-specific configuration
Fields:
| Field | Type | Default | Description |
|---|---|---|---|
publish | boolean | false | Enable publish operations |
subscribe | boolean | false | Enable subscribe operations |
Example:
asyncapi:
publish: true # Clients can publish to this resource
subscribe: true # Clients can subscribe to updates
validations (Optional)
Server side business rules. Absent, nothing changes anywhere.
# A relationship, declared on the property that holds it
schema:
items:
properties:
person:
schema:
$ref: "person.yaml#/schema"
references:
kind: persons # Required: the resource referenced
key: first_name # Optional: defaults to the target's schema key
value: person.first_name # Optional: defaults to the property name
on_missing: reject # Optional: reject (default) or ignore
immutable: false # Optional: can the reference be repointed
# Anything conditional, as a CEL expression
validations:
rules:
- name: postal_code_matches_city # Required, unique within the resource
description: <string> # Optional
methods: [post, put] # Optional: defaults to [post, put, patch]
refs: # Optional: the lookups the rule needs
postal:
kind: postal_codes
key: name # A path into the target, not a column name
value: postal_code
optional: false # Default: the rule fails if this finds nothing
expr: refs.postal.city == self.city # Required
error: # Optional
status: 422 # Defaults to 422
message: "{self.postal_code} is not in {self.city}."
examples: # Optional, generates tests
- self: {postal_code: K1A, city: Ottawa}
refs: {postal: {city: Ottawa}}
expect: pass
An expression sees self (the resource as it will be), old (as it currently is), refs (what the resolver found) and ctx (whatever the caller passes). Use methods, not on: YAML parses a bare on: key as the boolean true.
Lookups are required by default, so a rule whose lookup finds nothing fails rather than blowing up in the expression. Mark a lookup optional: true for a rule that is about absence, and guard it with has().
See Validations for the full reference.
JSON Schema Types
Common JSON Schema types used in schema.items.properties:
String
field_name:
type: string
minLength: 1 # Optional
maxLength: 100 # Optional
pattern: '^[a-z]+$' # Optional regex
format: email # Optional format
enum: [val1, val2] # Optional enumeration
default: "value" # Optional default
Common Formats:
email- Email addressuri- URI/URLuuid- UUIDdate- ISO 8601 date (YYYY-MM-DD)date-time- ISO 8601 datetimeipv4- IPv4 addressipv6- IPv6 address
Number / Integer
field_name:
type: integer # or "number" for floats
minimum: 0 # Optional
maximum: 100 # Optional
multipleOf: 5 # Optional
exclusiveMinimum: 0 # Optional
default: 10 # Optional
Boolean
field_name:
type: boolean
default: false # Optional
Array
field_name:
type: array
items:
type: string # or object, number, etc.
minItems: 0 # Optional
maxItems: 10 # Optional
uniqueItems: true # Optional
Object
field_name:
type: object
properties:
nested_field:
type: string
required: [nested_field]
additionalProperties: false # Optional
Null / Optional
# Nullable field
field_name:
type: [string, "null"]
# Optional field (not in required array)
properties:
optional_field:
type: string
required: [] # optional_field not listed
Validation Keywords
Common JSON Schema validation keywords:
| Keyword | Types | Description | Example |
|---|---|---|---|
minLength | string | Minimum length | minLength: 1 |
maxLength | string | Maximum length | maxLength: 100 |
pattern | string | Regex pattern | pattern: '^[A-Z]' |
format | string | String format | format: email |
enum | any | Fixed values | enum: [a, b, c] |
minimum | number | Minimum value | minimum: 0 |
maximum | number | Maximum value | maximum: 100 |
exclusiveMinimum | number | Exclusive min | exclusiveMinimum: 0 |
exclusiveMaximum | number | Exclusive max | exclusiveMaximum: 100 |
multipleOf | number | Multiple of | multipleOf: 10 |
minItems | array | Min array length | minItems: 1 |
maxItems | array | Max array length | maxItems: 10 |
uniqueItems | array | Unique items | uniqueItems: true |
required | object | Required fields | required: [name] |
additionalProperties | object | Allow extra props | additionalProperties: false |
Complete Example
kind: users
apiVersion: v1
metadata:
description: User account management API
version: "1.0.0"
version_in_path: false
methods:
resource: [get, post]
instance: [get, put, delete]
descriptions:
resource:
get: List all users with optional filtering
post: Create a new user account
instance:
get: Get user details
put: Update user account
delete: Delete user account
schema:
type: array
key:
name: user_id
description: Unique user identifier
schema:
type: string
format: uuid
query_params:
- name: status
description: Filter by account status
required: false
schema:
type: string
enum: [active, inactive, pending]
methods: [get]
- name: limit
description: Number of results to return
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
methods: [get]
items:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
description: User's full name
email:
type: string
format: email
maxLength: 254
description: Email address
status:
type: string
enum: [active, inactive, pending]
default: pending
description: Account status
age:
type: integer
minimum: 0
maximum: 150
description: User's age in years
tags:
type: array
items:
type: string
minLength: 1
maxLength: 50
maxItems: 10
uniqueItems: true
description: User tags
required: [name, email, status]
security:
scheme:
bearer_auth:
type: http
scheme: bearer
bearerFormat: JWT
resource: [post]
instance: [put, delete]
asyncapi:
publish: false
subscribe: true
Common Patterns
UUID Primary Key
key:
name: id
schema:
type: string
format: uuid
Integer Auto-Increment Key
key:
name: id
schema:
type: integer
minimum: 1
Slug/Handle Key
key:
name: slug
schema:
type: string
pattern: '^[a-z0-9-]+$'
minLength: 3
maxLength: 50
Timestamps
created_at:
type: string
format: date-time
description: Creation timestamp
updated_at:
type: string
format: date-time
description: Last update timestamp
Nested Address
address:
type: object
properties:
street: {type: string}
city: {type: string}
state: {type: string, pattern: '^[A-Z]{2}$'}
postal_code: {type: string, pattern: '^\d{5}(-\d{4})?$'}
required: [street, city, postal_code]
Enum with Description
status:
type: string
enum: [draft, published, archived]
description: |
Publication status:
- draft: Not yet published
- published: Publicly visible
- archived: No longer active
See Also
- Resource Schema Section - Detailed field documentation
- CLI Command Reference - Command syntax
- Examples - Complete working examples
- Common Patterns Cheat Sheet - Copy-paste patterns