Models
Learn how to define data models and their relationships in Query-2jz.
What are Models?
Models in Query-2jz define the structure of your data. They automatically generate database tables, API endpoints, and TypeScript types. No need to write resolvers or database migrations manually.
Key Benefits
Auto-generated CRUD operations
Automatic database schema creation
TypeScript type generation
Relationship handling
Validation rules
Index optimization
Basic Model Definition
Here's how to define a simple model in your query-2jz.config.js:
{
name: 'User',
fields: {
id: { type: 'id' },
name: { type: 'string', required: true },
email: { type: 'string', required: true, unique: true },
age: { type: 'number' },
isActive: { type: 'boolean', default: true },
createdAt: { type: 'date' },
updatedAt: { type: 'date' }
},
indexes: ['email', 'isActive']
}Field Types
Query-2jz supports various field types with built-in validation and database mapping.
Basic Types
stringText datanumberNumeric databooleanTrue/false valuesdateDate and timejsonJSON objectsSpecial Types
idUnique identifieruuidUUID stringemailEmail validationurlURL validationenumPredefined valuesField Options
Configure field behavior with various options.
Validation Options
{
name: {
type: 'string',
required: true, // Field is required
minLength: 2, // Minimum length
maxLength: 50, // Maximum length
pattern: '^[a-zA-Z ]+$' // Regex pattern
},
email: {
type: 'email',
unique: true, // Must be unique
required: true
},
age: {
type: 'number',
min: 0, // Minimum value
max: 120 // Maximum value
}
}Default Values
{
isActive: {
type: 'boolean',
default: true
},
status: {
type: 'enum',
values: ['pending', 'active', 'inactive'],
default: 'pending'
},
createdAt: {
type: 'date',
default: 'now' // Current timestamp
}
}Relationships
Define relationships between models for automatic join handling.
One-to-Many Relationship
A user can have many posts
// User model
{
name: 'User',
fields: {
id: { type: 'id' },
name: { type: 'string', required: true },
posts: {
type: 'relation',
model: 'Post',
many: true,
foreignKey: 'authorId'
}
}
}
// Post model
{
name: 'Post',
fields: {
id: { type: 'id' },
title: { type: 'string', required: true },
authorId: { type: 'string', required: true },
author: {
type: 'relation',
model: 'User',
foreignKey: 'authorId'
}
}
}Many-to-Many Relationship
Users can have many roles, roles can have many users
// User model
{
name: 'User',
fields: {
id: { type: 'id' },
name: { type: 'string', required: true },
roles: {
type: 'relation',
model: 'Role',
many: true,
through: 'UserRole' // Junction table
}
}
}
// Role model
{
name: 'Role',
fields: {
id: { type: 'id' },
name: { type: 'string', required: true },
users: {
type: 'relation',
model: 'User',
many: true,
through: 'UserRole'
}
}
}Indexes
Define database indexes for optimal query performance.
Single Field Indexes
{
name: 'User',
fields: {
email: { type: 'string', unique: true },
status: { type: 'string' }
},
indexes: ['email', 'status'] // Single field indexes
}Composite Indexes
{
name: 'Post',
fields: {
authorId: { type: 'string' },
status: { type: 'string' },
publishedAt: { type: 'date' }
},
indexes: [
'authorId',
'status',
{ fields: ['authorId', 'status'] }, // Composite index
{ fields: ['status', 'publishedAt'], unique: true } // Unique composite
]
}Model Hooks
Add custom logic that runs before or after model operations.
{
name: 'User',
fields: {
id: { type: 'id' },
name: { type: 'string', required: true },
email: { type: 'email', required: true }
},
hooks: {
beforeCreate: async (data) => {
// Hash password before creating user
if (data.password) {
data.password = await hashPassword(data.password)
}
return data
},
afterCreate: async (user) => {
// Send welcome email
await sendWelcomeEmail(user.email)
return user
},
beforeUpdate: async (data, oldData) => {
// Validate business rules
if (data.status === 'inactive' && oldData.status === 'active') {
// Check if user has active posts
const activePosts = await Post.count({ authorId: oldData.id, status: 'active' })
if (activePosts > 0) {
throw new Error('Cannot deactivate user with active posts')
}
}
return data
}
}
}Best Practices
Do
- • Use descriptive field names
- • Add indexes for frequently queried fields
- • Use appropriate field types
- • Define relationships explicitly
- • Add validation rules
- • Use hooks for business logic
- • Keep models focused and cohesive
Don't
- • Create overly complex models
- • Skip validation rules
- • Forget to add indexes
- • Use generic field names
- • Mix unrelated data in one model
- • Ignore relationship constraints
- • Hardcode business logic in models