Server Issues
Solutions for server composable and BFF pattern problems.
Import Errors
Cannot Find Server Composable
// server/api/pets.ts
import { getServerPet } from '~/server/composables/pets'
// Cannot find moduleCause: Server composables not generated or wrong path
Solution:
# 1. Generate server composables
echo nuxtServer | nxh generate -i swagger.yaml -o ./server/composables
# 2. Verify file exists
ls server/composables/pets.ts
# 3. Use correct import path
import { getServerPet } from '../composables/pets'Type Module Errors
import { getServerPet } from '../composables/pets'
// ❌ Cannot use import statement outside moduleCause: Server files need proper TypeScript/ES module setup
Solution:
// tsconfig.json
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler"
}
}H3Event Issues
Event Parameter Missing
// server/api/pets/[id].ts
export default defineEventHandler(async () => {
const pet = await getServerPet(1) // ❌ Missing event parameter
return pet
})Cause: Server composables require H3Event as first parameter
Solution:
// server/api/pets/[id].ts
export default defineEventHandler(async (event) => {
const id = Number(event.context.params?.id)
const pet = await getServerPet(event, id) // ✅ Pass event
return pet
})Cannot Read Properties of Event
const headers = event.headers
// ❌ Property 'headers' does not exist on type 'H3Event'Cause: Wrong API for H3Event
Solution:
import { getHeaders, getHeader } from 'h3'
// ✅ Use H3 utilities
const headers = getHeaders(event)
const auth = getHeader(event, 'authorization')Authentication Issues
Authorization Header Not Forwarded
// server/api/pets.ts
export default defineEventHandler(async (event) => {
const pet = await getServerPet(event, 1)
// API returns 401
})Cause: Headers not forwarded to backend API
Solution:
import { getProxyHeaders } from '../utils/headers'
export default defineEventHandler(async (event) => {
const headers = getProxyHeaders(event)
const pet = await getServerPet(event, 1, {
headers // ✅ Forward headers
})
return pet
})Missing Auth Token
// Backend API returns 401Cause: Token not in request or wrong header
Solution:
// utils/headers.ts
import { getHeaders } from 'h3'
export function getProxyHeaders(event: H3Event) {
const headers = getHeaders(event)
return {
authorization: headers.authorization, // ✅ Forward auth header
'x-api-key': process.env.API_KEY // ✅ Add server API key
}
}
// server/api/pets.ts
export default defineEventHandler(async (event) => {
const pet = await getServerPet(event, 1, {
headers: getProxyHeaders(event)
})
return pet
})Cannot Access User Context
const user = event.context.user // ❌ undefinedCause: Auth middleware not setting user context
Solution:
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const token = getHeader(event, 'authorization')
if (token) {
// Verify token and get user
const user = await verifyToken(token)
event.context.user = user // ✅ Set context
}
})
// server/api/pets.ts
export default defineEventHandler(async (event) => {
const user = event.context.user // ✅ Now available
if (!user) {
throw createError({
statusCode: 401,
message: 'Unauthorized'
})
}
return await getServerPets(event)
})Error Handling
Errors Not Properly Returned
export default defineEventHandler(async (event) => {
try {
return await getServerPet(event, 1)
} catch (error) {
return { error: error.message } // ❌ Wrong format
}
})Cause: Should use createError from H3
Solution:
import { createError } from 'h3'
export default defineEventHandler(async (event) => {
try {
return await getServerPet(event, 1)
} catch (error: any) {
// ✅ Use createError
throw createError({
statusCode: error.statusCode || 500,
message: error.message || 'Internal Server Error',
data: error.data
})
}
})Backend Errors Not Transformed
// Backend returns: { "error_code": "NOT_FOUND" }
// Want to return: { "message": "Pet not found" }Cause: Error response needs transformation
Solution:
export default defineEventHandler(async (event) => {
try {
return await getServerPet(event, 1)
} catch (error: any) {
// ✅ Transform backend errors
const message = error.statusCode === 404
? 'Pet not found'
: 'Failed to fetch pet'
throw createError({
statusCode: error.statusCode || 500,
message
})
}
})Query Parameters
Query Params Not Passed
// Client: /api/pets?limit=10
// Server composable doesn't receive paramsCause: Query params not extracted and passed
Solution:
import { getQuery } from 'h3'
export default defineEventHandler(async (event) => {
const query = getQuery(event) // ✅ Get query params
const pets = await getServerPets(event, {
query: {
limit: Number(query.limit) || 10,
offset: Number(query.offset) || 0
}
})
return pets
})Query Validation Failing
const query = getQuery(event)
const limit = query.limit // ❌ Type: string | string[] | undefinedCause: Query params need validation
Solution:
import { getQuery } from 'h3'
export default defineEventHandler(async (event) => {
const query = getQuery(event)
// ✅ Validate and convert
const limit = Math.max(1, Math.min(100, Number(query.limit) || 10))
const offset = Math.max(0, Number(query.offset) || 0)
return await getServerPets(event, {
query: { limit, offset }
})
})Response Transformation
Cannot Modify Response
export default defineEventHandler(async (event) => {
const pet = await getServerPet(event, 1)
pet.newField = 'value' // ❌ Cannot add to readonly
return pet
})Cause: Response type is readonly
Solution:
export default defineEventHandler(async (event) => {
const pet = await getServerPet(event, 1)
// ✅ Create new object
return {
...pet,
newField: 'value'
}
})Sensitive Data Not Filtered
// Accidentally returning password hashCause: Backend returns sensitive fields
Solution:
export default defineEventHandler(async (event) => {
const user = await getServerUser(event, 1)
// ✅ Filter sensitive fields
const { password, apiKey, ...safeUser } = user
return safeUser
})Performance Issues
Slow BFF Routes
export default defineEventHandler(async (event) => {
// Multiple serial requests
const pet = await getServerPet(event, 1)
const owner = await getServerOwner(event, pet.ownerId)
const vet = await getServerVet(event, pet.vetId)
return { pet, owner, vet }
})Cause: Serial requests instead of parallel
Solution:
export default defineEventHandler(async (event) => {
const petId = 1
// ✅ Parallel requests
const [pet, owner, vet] = await Promise.all([
getServerPet(event, petId),
getServerOwner(event, petId),
getServerVet(event, petId)
])
return { pet, owner, vet }
})No Caching
// Same request called multiple timesCause: No caching layer
Solution:
import { defineCachedEventHandler } from '#nitro'
export default defineCachedEventHandler(
async (event) => {
return await getServerPets(event)
},
{
maxAge: 60, // ✅ Cache for 60 seconds
getKey: (event) => {
const query = getQuery(event)
return `pets-${query.limit}-${query.offset}`
}
}
)Route Parameters
Cannot Access Route Params
// /api/pets/[id].ts
const id = event.params.id // ❌ Property 'params' does not existCause: Wrong API for accessing route params
Solution:
// ✅ Use event.context.params
export default defineEventHandler(async (event) => {
const id = Number(event.context.params?.id)
if (!id) {
throw createError({
statusCode: 400,
message: 'Invalid ID'
})
}
return await getServerPet(event, id)
})Params Type Wrong
const id = event.context.params?.id // Type: string
// Need: numberCause: Route params are strings
Solution:
export default defineEventHandler(async (event) => {
const idStr = event.context.params?.id
// ✅ Validate and convert
const id = Number(idStr)
if (isNaN(id)) {
throw createError({
statusCode: 400,
message: 'ID must be a number'
})
}
return await getServerPet(event, id)
})CORS Issues
CORS Errors in Development
Access to fetch at 'http://localhost:3000/api/pets' blocked by CORSCause: CORS not configured
Solution:
// server/middleware/cors.ts
export default defineEventHandler((event) => {
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type,Authorization'
})
if (event.method === 'OPTIONS') {
return ''
}
})Debugging
Enable Server Logging
export default defineEventHandler(async (event) => {
console.log('→ Request:', event.path)
console.log('→ Method:', event.method)
console.log('→ Headers:', getHeaders(event))
console.log('→ Query:', getQuery(event))
try {
const result = await getServerPet(event, 1)
console.log('✓ Success:', result)
return result
} catch (error) {
console.error('✗ Error:', error)
throw error
}
})Check Nitro Logs
# Development mode shows all server logs
npm run dev
# Check for:
# - Request paths
# - Error messages
# - Console.logs from server