Models are JSON files that describe your domain. Clay reads these models and uses them with templates to generate code. Think of models as the blueprint for what you want to build.
{
"name": "user-service",
"generators": ["./generators/api"],
"model": {
"types": [
{
"name": "User",
"fields": [
{ "name": "id", "type": "string" },
{ "name": "email", "type": "string" },
{ "name": "name", "type": "string" }
]
}
]
}
}
The name of your model/project:
"name": "user-service"
Array of generators to run on this model:
"generators": [
"./generators/api",
"./generators/frontend",
"clay-model-documentation"
]
Your actual domain model structure. Can be any JSON structure:
"model": {
"types": [...],
"services": [...],
"config": {...}
}
Apply JavaScript functions to transform parts of your model:
{
"name": "order-service",
"mixins": ["mixins/add-timestamps.js"],
"model": {
"types": [
{
"name": "Order",
"mixin": ["add-timestamps"],
"fields": [...]
}
]
}
}
Mixin file example (mixins/add-timestamps.js):
module.exports = function(type) {
type.fields = type.fields || [];
type.fields.push(
{ name: 'createdAt', type: 'timestamp' },
{ name: 'updatedAt', type: 'timestamp' }
);
return type;
};
Split large models into multiple files:
{
"name": "my-app",
"model": {
"types": [
{ "include": "entities/user.json" },
{ "include": "entities/order.json" }
],
"events": [
{ "include": "events/user-events.json" }
]
}
}
Included file (entities/user.json):
{
"name": "User",
"fields": [
{ "name": "id", "type": "string" },
{ "name": "email", "type": "string" }
]
}
Recommended directory structure:
clay/
├── model.json # Main model file
├── mixins/ # Reusable transformations
│ ├── timestamps.js
│ └── validation.js
├── entities/ # Split model files
│ ├── user.json
│ ├── order.json
│ └── product.json
└── generators/ # Custom generators
└── api/
├── generator.json
└── templates/
Generators use JSONPath to select parts of your model. Common patterns:
| Pattern | Selects |
|---|---|
$.model.types[*] |
All types |
$.model.types[*].fields[*] |
All fields of all types |
$.model.types[?(@.isActive)] |
Types where isActive is true |
$.model.types[*].fields[?(@.type=='array')] |
Array-type fields |
Test JSONPath expressions with:
clay test-path ./clay/model.json "$.model.types[*].fields[*]"
{
"name": "ecommerce",
"generators": ["./generators/api", "./generators/frontend"],
"mixins": ["mixins/timestamps.js", "mixins/validation.js"],
"model": {
"types": [
{
"name": "Product",
"mixin": ["timestamps", "validation"],
"fields": [
{ "name": "id", "type": "string", "required": true },
{ "name": "name", "type": "string", "required": true },
{ "name": "price", "type": "number", "required": true },
{ "name": "description", "type": "string" },
{ "name": "category", "type": "string", "required": true }
],
"commands": [
{
"name": "updatePrice",
"parameters": [
{ "name": "newPrice", "type": "number" }
],
"raises": "price_updated"
}
]
},
{
"name": "Order",
"mixin": ["timestamps"],
"fields": [
{ "name": "id", "type": "string", "required": true },
{ "name": "userId", "type": "string", "required": true },
{ "name": "items", "type": "array", "required": true },
{ "name": "total", "type": "number", "required": true },
{ "name": "status", "type": "string", "required": true }
]
}
]
}
}
include to split large models into manageable files
mixin for common properties (timestamps, audit
fields, etc.)
clay watch command
during development. It automatically regenerates code whenever you
modify your model!