Nested Relations
Nested Relations
Nuxt Auto API supports advanced nested relation loading with field selection, filtering, and pagination at each level of nesting.
Basic Nested Relations
The simplest form uses dot notation to load nested relations:
// GET /api/users?include=posts.comments.author
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
{
"id": 1,
"title": "Hello World",
"comments": [
{
"id": 1,
"body": "Great post!",
"author": {
"id": 2,
"name": "Jane Smith"
}
}
]
}
]
}
]
}
Field Selection
Select specific fields from relations using bracket syntax:
// GET /api/users?include=posts[id,title,createdAt]
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
{
"id": 1,
"title": "Hello World",
"createdAt": "2024-01-01T00:00:00Z"
// Only selected fields included
}
]
}
]
}
Filtering Relations
Filter related records using curly brace syntax:
// GET /api/users?include=posts{filter:{published:true}}
// Only published posts will be included
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
// Only posts where published = true
]
}
]
}
Advanced Filtering
Use the same filter operators as the main list endpoint:
// GET /api/users?include=posts{filter:{createdAt:{$gte:"2024-01-01"}}}
// GET /api/users?include=posts{filter:{title:{$contains:"nuxt"}}}
Pagination on Relations
Limit and offset related records:
// GET /api/users?include=posts{limit:5}
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
// Only first 5 posts
]
}
]
}
With offset:
// GET /api/users?include=posts{limit:5,offset:10}
Combining Features
Combine field selection, filtering, and pagination:
// GET /api/users?include=posts[id,title]{limit:10,filter:{published:true}}
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
{
"id": 1,
"title": "Hello World"
// Only id and title, only published, max 10
}
]
}
]
}
Deep Nesting
Combine all features with deep nesting:
// GET /api/users?include=posts[id,title]{limit:5,filter:{published:true}}.comments[id,body]{limit:3}.author[id,name]
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [
{
"id": 1,
"title": "Hello World",
"comments": [
{
"id": 1,
"body": "Great post!",
"author": {
"id": 2,
"name": "Jane Smith"
}
}
]
}
]
}
]
}
Multiple Relations
Include multiple relations at the same level:
// GET /api/users?include=posts[id,title],comments[id,body],profile[bio,avatar]
{
"data": [
{
"id": 1,
"name": "John Doe",
"posts": [...],
"comments": [...],
"profile": {
"bio": "...",
"avatar": "..."
}
}
]
}
Configuration
Configure nested relations behavior in nuxt.config.ts:
export default defineNuxtConfig({
autoApi: {
relations: {
// Maximum nesting depth (default: 3)
maxDepth: 5,
// Allow field selection (default: true)
allowFieldSelection: true,
// Allow filtering on relations (default: true)
allowFiltering: true,
// Allow pagination on relations (default: true)
allowPagination: true,
}
}
})
Max Depth Protection
To prevent performance issues and circular dependencies, nested relations are limited to a maximum depth (default: 3 levels).
Exceeding the max depth will log a warning and stop further nesting:
// If maxDepth is 3, this will only nest 3 levels deep
// GET /api/a?include=b.c.d.e.f
// Will load: a -> b -> c -> d (stops here)
Authorization on Nested Relations
Authorization checks apply at each level:
- Collection-level: User must have
readpermission for each related resource - Object-level: Object-level authorization functions execute for each related record
- Field-level: Field permissions apply to nested relation fields
Example:
// If user doesn't have permission to read 'posts', this will fail:
// GET /api/users?include=posts
// If user can't read certain fields in posts, they'll be filtered out:
// GET /api/users?include=posts[id,title,secret]
// 'secret' field will be omitted if user doesn't have permission
Performance Considerations
Database Queries
Each level of nesting may result in additional database queries. The exact number depends on your database client and Drizzle's query optimization.
Indexes
For optimal performance with filtering on relations:
-- Index the fields you filter by
CREATE INDEX idx_posts_published ON posts(published);
CREATE INDEX idx_posts_created_at ON posts(created_at);
Field Selection Benefits
Selecting only needed fields reduces:
- Network bandwidth
- JSON serialization time
- Client memory usage
// Good: Only fetch what you need
include=posts[id,title]
// Less efficient: Fetch everything
include=posts
Pagination Benefits
Limit relation results to avoid loading huge datasets:
// Good: Limit related records
include=posts{limit:10}
// Potentially slow: Load all posts for each user
include=posts
Common Patterns
Blog Posts with Recent Comments
GET /api/posts?include=comments[id,body,createdAt]{limit:5,filter:{createdAt:{$gte:"2024-01-01"}}}.author[name,avatar]
Users with Recent Activity
GET /api/users?include=posts[id,title]{limit:3},comments[id,body]{limit:5}
Nested Categories
GET /api/categories?include=subcategories.products[id,name,price]{limit:10}
Error Handling
Invalid include syntax returns a 400 error:
// Invalid: Unknown relation
GET /api/users?include=nonexistent
// Response: 400 Bad Request - "Unknown relation: nonexistent"
// Invalid: Exceeds max depth
GET /api/users?include=a.b.c.d.e.f.g
// Response: Warning logged, nesting stops at max depth
TypeScript Support
The frontend composables infer nested types:
const { data } = await useAutoApiFetch('users', {
query: {
include: 'posts.comments.author'
}
})
// TypeScript knows data includes nested relations
data.value[0].posts[0].comments[0].author.name
Comparison with GraphQL
While not as flexible as GraphQL, nested relations provide similar capabilities:
| Feature | Nested Relations | GraphQL |
|---|---|---|
| Field selection | ✅ | ✅ |
| Nested loading | ✅ | ✅ |
| Filtering | ✅ | ✅ |
| Pagination | ✅ | ✅ |
| Type safety | ✅ (TypeScript) | ✅ |
| No schema required | ✅ | ❌ |
| Learning curve | Low | Medium |
Best Practices
- Always select fields: Don't load more data than needed
- Use pagination: Limit related records to prevent performance issues
- Filter early: Apply filters at the relation level when possible
- Monitor depth: Keep nesting to 2-3 levels for best performance
- Index filter fields: Create database indexes for filtered fields
- Cache responses: Use HTTP caching for frequently accessed nested data
Migration from Simple Includes
Existing code with simple includes continues to work:
// Old syntax (still works)
GET /api/users?include=posts,comments
// New enhanced syntax (more powerful)
GET /api/users?include=posts[id,title]{limit:10},comments[id,body]{limit:5}
The system automatically detects and handles both syntaxes.
Better-Auth Integration
Integrate @websideproject/nuxt-auto-api with better-auth for a complete authentication solution with sessions, OAuth, organizations, and more.
Bulk Operations
Nuxt Auto API provides atomic bulk operations for creating, updating, and deleting multiple records in a single request.