Error Handling
Understanding and handling errors in queries and operations
Error Handling
Collections provides comprehensive error handling with typed error codes and messages. Learn how to handle errors effectively in your application.
Error Types
Prop
Type
Basic Error Handling
Try-Catch Pattern
try {
const user = await collections.users.create({
data: {
name: 'John Doe',
email: 'john@example.com'
}
})
console.log('User created:', user)
} catch (error) {
console.error('Error creating user:', error.message)
}Error Code Handling
try {
const user = await collections.users.create({
data: {
name: 'John Doe',
email: 'john@example.com'
}
})
} catch (error) {
switch (error.code) {
case 'VALIDATION_ERROR':
console.log('Validation failed:', error.messages)
break
case 'UNIQUE_CONSTRAINT':
console.log('Email already exists')
break
case 'DATABASE_ERROR':
console.log('Database error:', error.message)
break
default:
console.log('Unknown error:', error)
}
}Validation Errors
Validation errors occur when data doesn't meet field requirements.
Structure
{
code: 'VALIDATION_ERROR',
message: 'Validation failed',
errors: {
email: [
'Email is required',
'Invalid email format'
],
age: [
'Must be at least 18 years old'
]
}
}Handling Validation Errors
const create_user = async (data: UserData) => {
try {
const user = await collections.users.create({ data })
return { success: true, user }
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
// Return formatted errors
return {
success: false,
errors: error.errors
}
}
throw error
}
}
// Usage
const result = await createUser({
name: '',
email: 'invalid',
age: 15
})
if (!result.success) {
console.log('Validation errors:', result.errors)
// {
// email: ['Email is required', 'Invalid email format'],
// age: ['Must be at least 18 years old']
// }
}Not Found Errors
findUnique/findFirst Can Return Null
const user = await collections.users.findUnique({
where: { id: 999 }
})
if (user === null) {
console.log('User not found')
}Throw on Not Found
You can use non-null assertion or check manually:
const getUser = async (id: number) => {
const user = await collections.users.findUnique({
where: { id }
})
if (!user) {
throw new Error(`User with id ${id} not found`)
}
return user
}
// Or throw automatically
const user = await collections.users.findUnique({
where: { id: 999 }
})!
// This will throw if user is nullCustom Not Found Error
class NotFoundError extends Error {
code = 'NOT_FOUND'
constructor(resource: string, id: number) {
super(`${resource} with id ${id} not found`)
}
}
const getUserOrThrow = async (id: number) => {
const user = await collections.users.findUnique({
where: { id }
})
if (!user) {
throw new NotFoundError('User', id)
}
return user
}Unique Constraint Errors
Handling Duplicate Values
try {
const user = await collections.users.create({
data: {
email: 'existing@example.com' // Already exists
}
})
} catch (error) {
if (error.code === 'UNIQUE_CONSTRAINT') {
console.log('Email already taken')
// Show user-friendly message
return { error: 'This email is already registered' }
}
}Checking Before Insert
const create_user_unique = async (email: string, name: string) => {
// Check if exists
const existing = await collections.users.findFirst({
where: { email: { equals: email } }
})
if (existing) {
return {
success: false,
error: 'Email already exists'
}
}
// Safe to create
const user = await collections.users.create({
data: { email, name }
})
return { success: true, user }
}Foreign Key Errors
Handling Related Records
try {
const comment = await collections.comments.create({
data: {
content: 'Great post!',
postId: 999 // Post doesn't exist
}
})
} catch (error) {
if (error.code === 'FOREIGN_KEY_CONSTRAINT') {
console.log('Referenced post does not exist')
return {
error: 'The post you are commenting on does not exist'
}
}
}Checking Relations First
const create_comment_safe = async (postId: number, content: string) => {
// Verify post exists
const post = await collections.posts.findUnique({
where: { id: postId }
})
if (!post) {
return {
success: false,
error: 'Post not found'
}
}
// Safe to create comment
const comment = await collections.comments.create({
data: { postId, content }
})
return { success: true, comment }
}Database Errors
Connection Issues
try {
const users = await collections.users.findMany()
} catch (error) {
if (error.code === 'DATABASE_ERROR') {
if (error.message.includes('connection')) {
console.log('Database connection failed')
// Retry logic or show maintenance page
} else {
console.log('Database error:', error.message)
}
}
}Retry Logic
const withRetry = async <T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (error.code === 'DATABASE_ERROR' && i < maxRetries - 1) {
console.log(`Retry ${i + 1}/${maxRetries}`)
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
continue
}
throw error
}
}
throw new Error('Max retries exceeded')
}
// Usage
const users = await withRetry(() =>
collections.users.findMany()
)Error Response Format
API Responses
For API routes, return consistent error responses:
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
try {
const data = await request.json()
const user = await collections.users.create({ data })
return NextResponse.json({
success: true,
data: user
})
} catch (error) {
return NextResponse.json(
{
success: false,
error: {
code: error.code,
message: error.message,
...(error.code === 'VALIDATION_ERROR' && {
fields: error.errors
})
}
},
{ status: 400 }
)
}
}Client-Side Handling
// Client code
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data)
})
const result = await response.json()
if (!result.success) {
if (result.error.code === 'VALIDATION_ERROR') {
// Show field errors
Object.keys(result.error.fields).forEach(field => {
console.log(`${field}:`, result.error.fields[field])
})
} else {
// Show general error
console.log(result.error.message)
}
}Global Error Handler
Create a Utility Function
import { collections } from './config/database'
type CollectionOperationResult<T> =
| { success: true; data: T }
| { success: false; error: string; code?: string; fields?: Record<string, string[]> }
export const handleCollectionOperation = async <T>(
operation: () => Promise<T>
): Promise<CollectionOperationResult<T>> => {
try {
const data = await operation()
return { success: true, data }
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
return {
success: false,
error: 'Validation failed',
code: error.code,
fields: error.errors
}
}
if (error.code === 'UNIQUE_CONSTRAINT') {
return {
success: false,
error: 'A record with this value already exists',
code: error.code
}
}
if (error.code === 'NOT_FOUND') {
return {
success: false,
error: 'Record not found',
code: error.code
}
}
return {
success: false,
error: 'An unexpected error occurred',
code: 'UNKNOWN_ERROR'
}
}
}
// Usage
const result = await handleCollectionOperation(() =>
collections.users.create({
data: { name: 'John', email: 'john@example.com' }
})
)
if (!result.success) {
console.log('Error:', result.error)
if (result.fields) {
console.log('Field errors:', result.fields)
}
}Best Practices
Error Codes Reference
Prop
Type
Next Steps
- Queries - Basic CRUD operations
- Filters - Advanced filtering operators
- Pagination - Pagination and sorting strategies