Throwing Errors in Middleware
This section covers the practical tools you'll use every day when handling errors in middleware.
Your Main Tool: context.createHttpError()
When you need to throw an error in a middleware endpoint, use context.createHttpError(). It ensures the error has:
- A proper HTTP status code (404, 409, 500, etc.)
- A human-readable message
- Structured data the frontend can parse
When to use it
Use context.createHttpError() in these situations:
- Missing or invalid request parameters — Throw 400 Bad Request
- Vendor returns success with error indicator — Check response payload, throw appropriate error
- Custom error messages for frontend — Transform technical errors into user-friendly ones
Basic pattern
// Check request parameters
if (!args.id) {
throw context.createHttpError({
statusCode: 400,
message: 'Product ID is required',
data: { field: 'id' },
});
}
Always include the data property with structured information. Your frontend can use this to show specific error messages or highlight form fields.
Error Adapters for HTTP Clients
When your integration calls external APIs, errors need to be normalized to HttpError with proper status codes. Alokai provides error adapter packages for different HTTP clients (Axios, Apollo Client, etc.).
How Adapters Work
Adapters are typically configured once when creating your HTTP client instance. They automatically intercept and normalize all errors:
// Setup in index.server.ts or client factory
const client = adapter.withErrorNormalizer(
httpClient,
{
extractMessage: (payload) => {
// Vendor-specific message extraction
return payload?.errors?.[0]?.message;
},
},
);
// Now all errors from this client are automatically normalized
When Adapters Are Already Configured
Most integrations configure error adapters in index.server.ts or client factory files. Once configured:
- No try/catch needed in your endpoint methods
- Errors are automatically normalized with correct status codes
- Custom message extraction is already configured per vendor
// In your endpoint - no error handling needed
export const getProducts = async (context, args) => {
const { api } = await context.getApiClient();
// Errors automatically normalized by adapter
return await api.getProducts({ categoryId: args.categoryId });
};
Check your integration's setup files (index.server.ts, apolloClient.ts) to see if adapters are already configured. If they are, you don't need to add error handling in most endpoint methods.
When to Use context.createHttpError()
Use context.createHttpError() for cases adapters don't handle:
- Missing or invalid request parameters — Throw 400 Bad Request
- Vendor returns success with error indicator — Check response payload, throw appropriate error
- Custom error messages for frontend — Transform technical errors into user-friendly ones
Example: SAP out of stock
SAP returns a success response but includes statusCode: 'noStock' in cartModifications:
const { data } = await api.addCartEntry({ ... });
// Check for error indicator in successful response
if (data?.cartModifications?.find(m => m.statusCode === 'noStock')) {
throw context.createHttpError({
statusCode: 409, // Conflict
message: 'Product is out of stock',
data: { errors: [{ type: 'InsufficientStockError' }] },
});
}
Why 409 Conflict? This is a business constraint, not a server error. Using 409 allows your frontend to show a specific "out of stock" message instead of generic "something went wrong".
Domain Errors: Use Sparingly
Middleware can also throw domain errors like ValidationError, NotFoundError, and UnauthorizedError. These automatically map to HTTP status codes:
ValidationError→ 422 Unprocessable Entity (or 400 if thrown without issues)NotFoundError→ 404 Not FoundUnauthorizedError→ 401 Unauthorized
When to use domain errors
- Middleware-level validation (rare — most validation happens in frontend or external API)
- Shared validation logic between multiple endpoints
Don't overuse domain errors in middleware. Most of the time, context.createHttpError() is simpler and more explicit.
ValidationError with Standard Schema
ValidationError has special support for schema validation libraries like zod, valibot, and arktype. Uncaught schema validation errors are automatically normalized to ValidationError with HTTP 422 status.
import { z } from 'zod';
// ZodError is automatically normalized to ValidationError
const schema = z.object({ email: z.email() });
const validated = schema.parse(args); // Throws ZodError if invalid
See Validation Errors for full documentation on schema validation support.
Custom Error Types
You can create custom errors by extending AppError:
import { AppError } from '@alokai/connect/middleware';
export class PaymentGatewayError extends AppError {
constructor(gatewayCode: string, cause?: unknown) {
super('Payment gateway returned an error', {
cause,
data: { gatewayCode },
});
}
}
When to create custom errors
- You need to catch and retry specific error types
- You're building a shared library used across integrations
- Standard errors don't clearly express what failed
Most middleware endpoints don't need custom errors — context.createHttpError() is usually sufficient.
Common Mistakes
❌ Throwing plain Error objects
// DON'T
throw new Error('Product not found');
Problem: Middleware can't extract a status code. Frontend gets 500 instead of 404.
Fix: Use context.createHttpError() or a domain error.
❌ Letting external errors escape
// DON'T
const { data } = await axios.get(url); // No try/catch!
Problem: If Axios throws, the error isn't normalized. Circuit breaker sees it as infrastructure failure.
Fix: Wrap in try/catch and use normalizeAxiosError().
❌ Exposing vendor details
// DON'T
throw context.createHttpError({
message: JSON.stringify(vendorError), // Leaks internal details
});
Problem: Exposes vendor-specific error codes and stack traces to frontend.
Fix: Create a user-friendly message. Store vendor details in cause for debugging.
Quick Reference
| Tool | When to Use |
|---|---|
context.createHttpError() | Missing params, vendor payload errors, custom messages |
| Error adapters | Configure once in client factory, auto-normalizes all errors |
ValidationError | Schema validation with zod/valibot (auto-maps to 422) |
NotFoundError | Resource not found (auto-maps to 404) |
Custom AppError | Specific retry logic, shared libraries |