Basic Usage Examples
This guide provides practical examples to get you started with Adonis ODM. We’ll cover the most common use cases and patterns you’ll encounter when building applications.
Setting Up Your First Model
User Model
// app/models/user.ts
import { BaseModel, column } from 'adonis-odm'
import { DateTime } from 'luxon'
export default class User extends BaseModel {
@column({ isPrimary: true })
declare _id: string
@column()
declare name: string
@column()
declare email: string
@column()
declare password: string
@column()
declare age: number
@column()
declare status: 'active' | 'inactive' | 'pending'
@column()
declare role: 'user' | 'admin' | 'moderator'
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime
}
Post Model
// app/models/post.ts
import { BaseModel, column, belongsTo } from 'adonis-odm'
import { DateTime } from 'luxon'
import type { BelongsTo } from 'adonis-odm/types'
import User from './user.js'
export default class Post extends BaseModel {
@column({ isPrimary: true })
declare _id: string
@column()
declare title: string
@column()
declare content: string
@column()
declare slug: string
@column()
declare authorId: string
@column()
declare isPublished: boolean
@column()
declare tags: string[]
@column()
declare viewCount: number
@column.dateTime()
declare publishedAt: DateTime | null
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
declare updatedAt: DateTime
@belongsTo(() => User)
declare author: BelongsTo<typeof User>
}
Basic CRUD Operations
Creating Records
// Create a single user
const user = await User.create({
name: 'John Doe',
email: '[email protected]',
password: 'hashedPassword',
age: 30,
status: 'active',
role: 'user'
})
console.log('Created user:', user._id)
// Create multiple users
const users = await User.createMany([
{
name: 'Alice Smith',
email: '[email protected]',
password: 'hashedPassword',
age: 28,
status: 'active',
role: 'user'
},
{
name: 'Bob Johnson',
email: '[email protected]',
password: 'hashedPassword',
age: 35,
status: 'pending',
role: 'moderator'
}
])
console.log(`Created ${users.length} users`)
Reading Records
// Find all users
const allUsers = await User.query().all()
// Find user by ID
const user = await User.find('507f1f77bcf86cd799439011')
if (user) {
console.log('Found user:', user.name)
}
// Find user by ID or throw error
const userOrFail = await User.findOrFail('507f1f77bcf86cd799439011')
// Find first user matching criteria
const activeUser = await User.query()
.where('status', 'active')
.first()
// Find all active users
const activeUsers = await User.query()
.where('status', 'active')
.all()
// Find users with specific roles
const admins = await User.query()
.whereIn('role', ['admin', 'moderator'])
.all()
// Find users by age range
const adults = await User.query()
.whereBetween('age', [18, 65])
.all()
Updating Records
// Update using model instance
const user = await User.findOrFail('507f1f77bcf86cd799439011')
user.name = 'John Smith'
user.age = 31
await user.save()
// Update using merge
const user2 = await User.findOrFail('507f1f77bcf86cd799439012')
user2.merge({
name: 'Jane Doe',
status: 'active'
})
await user2.save()
// Direct update without fetching
const affectedRows = await User.query()
.where('_id', '507f1f77bcf86cd799439011')
.update({
name: 'Updated Name',
updatedAt: DateTime.now()
})
// Bulk update
const bulkUpdate = await User.query()
.where('status', 'pending')
.update({
status: 'active'
})
console.log(`Updated ${bulkUpdate} users`)
Deleting Records
// Delete using model instance
const user = await User.findOrFail('507f1f77bcf86cd799439011')
await user.delete()
// Direct delete
const deletedCount = await User.query()
.where('_id', '507f1f77bcf86cd799439011')
.delete()
// Bulk delete
const bulkDelete = await User.query()
.where('status', 'inactive')
.where('createdAt', '<', DateTime.now().minus({ months: 6 }))
.delete()
console.log(`Deleted ${bulkDelete} inactive users`)
Working with Queries
Basic Filtering
// Simple where clauses
const activeUsers = await User.query()
.where('status', 'active')
.all()
// Multiple conditions (AND)
const youngAdults = await User.query()
.where('age', '>=', 18)
.where('age', '<', 30)
.where('status', 'active')
.all()
// OR conditions
const specialUsers = await User.query()
.where('role', 'admin')
.orWhere('role', 'moderator')
.all()
// Complex conditions with grouping
const complexQuery = await User.query()
.where('status', 'active')
.where((query) => {
query
.where('role', 'admin')
.orWhere((subQuery) => {
subQuery
.where('role', 'user')
.where('age', '>=', 21)
})
})
.all()
Sorting and Limiting
// Order by single field
const recentUsers = await User.query()
.orderBy('createdAt', 'desc')
.limit(10)
.all()
// Order by multiple fields
const sortedUsers = await User.query()
.orderBy('status', 'asc')
.orderBy('createdAt', 'desc')
.all()
// Pagination
const page = 1
const perPage = 20
const paginatedUsers = await User.query()
.where('status', 'active')
.orderBy('createdAt', 'desc')
.paginate(page, perPage)
console.log('Users:', paginatedUsers.data)
console.log('Total:', paginatedUsers.meta.total)
console.log('Current page:', paginatedUsers.meta.page)
Field Selection
// Select specific fields
const userSummary = await User.query()
.select('name', 'email', 'status')
.where('status', 'active')
.all()
// Exclude sensitive fields
const publicUsers = await User.query()
.select('*')
.deselect('password')
.all()
Working with Relationships
Creating Related Records
// Create post with author
const author = await User.findOrFail('507f1f77bcf86cd799439011')
const post = await Post.create({
title: 'My First Blog Post',
content: 'This is the content of my first blog post...',
slug: 'my-first-blog-post',
authorId: author._id,
isPublished: true,
tags: ['javascript', 'mongodb', 'adonis'],
viewCount: 0,
publishedAt: DateTime.now()
})
// Create post using relationship
const post2 = await author.related('posts').create({
title: 'Another Post',
content: 'More content...',
slug: 'another-post',
isPublished: false,
tags: ['tutorial'],
viewCount: 0
})
Querying with Relationships
// Preload author information
const postsWithAuthors = await Post.query()
.preload('author', (query) => {
query.select('name', 'email')
})
.where('isPublished', true)
.all()
// Access preloaded data
postsWithAuthors.forEach(post => {
console.log(`${post.title} by ${post.author.name}`)
})
// Query posts by author
const johnsPosts = await Post.query()
.whereHas('author', (query) => {
query.where('name', 'like', '%John%')
})
.all()
Aggregation Examples
Counting Records
// Simple count
const userCount = await User.query().count()
console.log(`Total users: ${userCount}`)
// Count with conditions
const activeUserCount = await User.query()
.where('status', 'active')
.count()
// Count by groups
const usersByRole = await User.query()
.groupBy('role')
.count()
Advanced Aggregation
// User statistics
const userStats = await User.query()
.aggregate([
{
$group: {
_id: '$role',
count: { $sum: 1 },
avgAge: { $avg: '$age' },
minAge: { $min: '$age' },
maxAge: { $max: '$age' }
}
},
{
$sort: { count: -1 }
}
])
console.log('User statistics by role:', userStats)
// Monthly user registration
const monthlyRegistrations = await User.query()
.aggregate([
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' }
},
count: { $sum: 1 }
}
},
{
$sort: {
'_id.year': -1,
'_id.month': -1
}
}
])
Error Handling
Common Error Patterns
// Handle not found errors
try {
const user = await User.findOrFail('invalid-id')
} catch (error) {
if (error.code === 'E_ROW_NOT_FOUND') {
console.log('User not found')
// Handle gracefully
} else {
throw error
}
}
// Handle validation errors
try {
const user = await User.create({
email: 'invalid-email' // This might fail validation
})
} catch (error) {
if (error.code === 'E_VALIDATION_FAILURE') {
console.log('Validation errors:', error.messages)
} else {
throw error
}
}
// Handle duplicate key errors
try {
const user = await User.create({
email: '[email protected]' // Email already exists
})
} catch (error) {
if (error.code === 11000) { // MongoDB duplicate key error
console.log('Email already exists')
} else {
throw error
}
}
Practical Examples
User Registration
async function registerUser(userData: {
name: string
email: string
password: string
age: number
}) {
try {
// Check if email already exists
const existingUser = await User.query()
.where('email', userData.email)
.first()
if (existingUser) {
throw new Error('Email already registered')
}
// Create new user
const user = await User.create({
...userData,
status: 'pending',
role: 'user'
})
console.log('User registered successfully:', user._id)
return user
} catch (error) {
console.error('Registration failed:', error.message)
throw error
}
}
// Usage
const newUser = await registerUser({
name: 'Jane Doe',
email: '[email protected]',
password: 'hashedPassword',
age: 25
})
Blog Post Management
async function createBlogPost(authorId: string, postData: {
title: string
content: string
tags: string[]
publish: boolean
}) {
try {
// Verify author exists
const author = await User.findOrFail(authorId)
// Generate slug from title
const slug = postData.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
// Create post
const post = await Post.create({
title: postData.title,
content: postData.content,
slug,
authorId: author._id,
isPublished: postData.publish,
tags: postData.tags,
viewCount: 0,
publishedAt: postData.publish ? DateTime.now() : null
})
console.log('Post created:', post.title)
return post
} catch (error) {
console.error('Failed to create post:', error.message)
throw error
}
}
// Usage
const newPost = await createBlogPost('507f1f77bcf86cd799439011', {
title: 'Getting Started with Adonis ODM',
content: 'In this post, we will explore...',
tags: ['adonis', 'mongodb', 'tutorial'],
publish: true
})
Search and Filter
async function searchPosts(filters: {
query?: string
author?: string
tags?: string[]
published?: boolean
page?: number
perPage?: number
}) {
const query = Post.query()
// Text search
if (filters.query) {
query.where((subQuery) => {
subQuery
.where('title', 'like', `%${filters.query}%`)
.orWhere('content', 'like', `%${filters.query}%`)
})
}
// Filter by author
if (filters.author) {
query.whereHas('author', (authorQuery) => {
authorQuery.where('name', 'like', `%${filters.author}%`)
})
}
// Filter by tags
if (filters.tags && filters.tags.length > 0) {
query.whereArrayContainsAny('tags', filters.tags)
}
// Filter by published status
if (filters.published !== undefined) {
query.where('isPublished', filters.published)
}
// Preload author
query.preload('author', (authorQuery) => {
authorQuery.select('name', 'email')
})
// Sort by most recent
query.orderBy('createdAt', 'desc')
// Paginate
const page = filters.page || 1
const perPage = filters.perPage || 10
return await query.paginate(page, perPage)
}
// Usage
const searchResults = await searchPosts({
query: 'mongodb',
tags: ['tutorial'],
published: true,
page: 1,
perPage: 20
})
console.log('Found posts:', searchResults.data.length)
console.log('Total posts:', searchResults.meta.total)
Next Steps
- Advanced Queries - Complex query patterns and optimization
- Embedded Documents - Working with nested data structures
- Transactions - Managing data consistency
- Query Builder Guide - Complete query building reference