Overview
The T1 Component Library API uses a consistent error handling strategy across all endpoints. Errors are caught by a global error middleware that formats responses uniformly and logs errors for debugging.
All errors follow this JSON structure:
{
"success" : false ,
"message" : "Descripción del error"
}
In development mode (NODE_ENV=development), the response includes a stack trace:
{
"success" : false ,
"message" : "Descripción del error" ,
"stack" : "Error: .. \n at ... \n at ..."
}
Stack traces are only included in development to prevent exposing internal details in production.
Error Middleware Implementation
The error middleware is defined in server/src/middlewares/error.middleware.ts:
error.middleware.ts:10-44
export const errorMiddleware = (
err : CustomError ,
req : Request ,
res : Response ,
_next : NextFunction
) : void => {
logger . error ( `Error: ${ err . message } ` , { stack: err . stack });
let statusCode = err . statusCode || 500 ;
let message = err . message || 'Error interno del servidor' ;
// Error de duplicado de MongoDB
if ( err . code === 11000 ) {
statusCode = 400 ;
const field = Object . keys ( err . keyValue || {})[ 0 ];
message = `El ${ field } ya está registrado` ;
}
// Error de validación de Mongoose
if ( err . name === 'ValidationError' ) {
statusCode = 400 ;
}
// Error de Cast de MongoDB (ID inválido)
if ( err . name === 'CastError' ) {
statusCode = 400 ;
message = 'ID inválido' ;
}
res . status ( statusCode ). json ({
success: false ,
message ,
... ( process . env . NODE_ENV === 'development' && { stack: err . stack })
});
};
Error Type Detection
The middleware handles specific error types:
MongoDB Duplicate Key (code: 11000): Returns 400 with field name
Mongoose Validation Error : Returns 400 for schema validation failures
MongoDB Cast Error : Returns 400 for invalid ObjectId formats
Custom Errors : Uses statusCode property if set
Unknown Errors : Defaults to 500
HTTP Status Codes
The API uses standard HTTP status codes:
Code Description When Used 200 OK Successful GET requests 201 Created Successful POST requests (resource created) 400 Bad Request Validation errors, invalid data, duplicate keys 401 Unauthorized Missing/invalid JWT token, incorrect credentials 404 Not Found Resource doesn’t exist 500 Internal Server Error Unexpected server errors, database errors 503 Service Unavailable Database disconnected or service degraded
Common Error Scenarios
Authentication Errors
Missing Token
Request:
GET /api/components/export
Response (401):
{
"success" : false ,
"message" : "Token no proporcionado"
}
Invalid or Expired Token
Request:
GET /api/components/export
Authorization : Bearer invalid_token
Response (401):
{
"success" : false ,
"message" : "Token inválido"
}
Invalid Credentials
Request:
POST /api/auth/login
Content-Type : application/json
{
"email" : "[email protected] " ,
"password" : "wrong_password"
}
Response (401):
{
"success" : false ,
"message" : "Credenciales inválidas"
}
Validation Errors
Missing Required Fields
Request:
POST /api/components/track
Content-Type : application/json
{
"nombre" : "Button"
// Missing "accion" field
}
Response (400):
{
"success" : false ,
"message" : "Datos inválidos"
}
Duplicate Email Registration
Request:
POST /api/auth/register
Content-Type : application/json
{
"nombre" : "Juan Pérez" ,
"email" : "[email protected] " ,
"password" : "password123"
}
Response (400):
{
"success" : false ,
"message" : "El email ya está registrado"
}
Invalid MongoDB ObjectId
Request:
GET /api/users/invalid_id
Response (400):
{
"success" : false ,
"message" : "ID inválido"
}
Server Errors
Database Connection Error
Request:
GET /api/components/stats
Response (500):
{
"success" : false ,
"message" : "Error interno del servidor"
}
Service Degraded (Health Check)
Request:
Response (503):
{
"success" : true ,
"status" : "degraded" ,
"timestamp" : "2025-11-27T12:00:00.000Z" ,
"uptime" : "120s" ,
"services" : {
"database" : {
"status" : "disconnected" ,
"connected" : false
}
},
"system" : {
"nodeVersion" : "v18.19.0" ,
"memory" : {
"heapUsed" : "45MB" ,
"heapTotal" : "65MB" ,
"rss" : "85MB"
}
}
}
Error Handling on the Client
Basic Try-Catch
import { api } from '@/lib/api' ;
try {
const response = await api . trackInteraction ({
nombre: 'Button' ,
accion: 'click' ,
tipo_usuario: 'anonymous'
});
console . log ( 'Success:' , response . data );
} catch ( error ) {
if ( error instanceof Error ) {
console . error ( 'Error:' , error . message );
}
}
Checking Response Status
const response = await fetch ( ` ${ API_URL } /components/track` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( payload ),
});
if ( ! response . ok ) {
const error = await response . json ();
switch ( response . status ) {
case 400 :
console . error ( 'Validation error:' , error . message );
break ;
case 401 :
console . error ( 'Unauthorized:' , error . message );
// Redirect to login
window . location . href = '/login' ;
break ;
case 500 :
console . error ( 'Server error:' , error . message );
break ;
default :
console . error ( 'Unknown error:' , error . message );
}
throw new Error ( error . message );
}
React Query Error Handling
import { useQuery } from '@tanstack/react-query' ;
import { api } from '@/lib/api' ;
function StatsComponent () {
const { data , error , isError , isLoading } = useQuery ({
queryKey: [ 'stats' ],
queryFn: api . getStats ,
retry : ( failureCount , error ) => {
// Don't retry on 4xx errors
if ( error instanceof Error && error . message . includes ( '401' )) {
return false ;
}
return failureCount < 3 ;
}
});
if ( isLoading ) {
return < div > Loading ...</ div > ;
}
if ( isError ) {
return (
< div className = "error" >
< h3 > Error fetching stats </ h3 >
< p >{error. message } </ p >
</ div >
);
}
return < div >{ /* Render stats */ } </ div > ;
}
Global Error Handler
Create a custom error handler for all API calls:
export class ApiError extends Error {
constructor (
public statusCode : number ,
message : string ,
public details ?: unknown
) {
super ( message );
this . name = 'ApiError' ;
}
}
export async function handleApiResponse < T >( response : Response ) : Promise < T > {
if ( ! response . ok ) {
const error = await response . json (). catch (() => ({
message: 'Unknown error occurred'
}));
throw new ApiError (
response . status ,
error . message || 'Request failed' ,
error
);
}
return response . json ();
}
Use it in your API calls:
export const api = {
trackInteraction : async ( payload : TrackingPayload ) => {
const response = await fetch ( ` ${ API_URL } /components/track` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( payload ),
});
return handleApiResponse < TrackingResponse >( response );
}
};
Best Practices
1. Always Handle Errors
// Bad - Error will crash the app
const data = await api . getStats ();
// Good - Error is caught and handled
try {
const data = await api . getStats ();
} catch ( error ) {
showErrorToast ( error . message );
}
2. Provide User-Friendly Messages
const ERROR_MESSAGES : Record < number , string > = {
400 : 'Please check your input and try again' ,
401 : 'Please log in to continue' ,
404 : 'The requested resource was not found' ,
500 : 'Something went wrong. Please try again later' ,
503 : 'Service is temporarily unavailable'
};
try {
await api . trackInteraction ( payload );
} catch ( error ) {
const statusCode = error . statusCode || 500 ;
const userMessage = ERROR_MESSAGES [ statusCode ] || 'An error occurred' ;
showToast ( userMessage );
}
3. Log Errors for Debugging
try {
await api . trackInteraction ( payload );
} catch ( error ) {
// Log for debugging
console . error ( 'API Error:' , {
endpoint: '/components/track' ,
payload ,
error: error . message ,
statusCode: error . statusCode
});
// Show user-friendly message
showErrorToast ( 'Failed to track interaction' );
}
4. Handle Authentication Errors Globally
// Set up a response interceptor
async function apiCall ( url : string , options : RequestInit ) {
const response = await fetch ( url , options );
if ( response . status === 401 ) {
// Clear auth state
localStorage . removeItem ( 'token' );
// Redirect to login
window . location . href = '/login' ;
throw new Error ( 'Session expired' );
}
return response ;
}
5. Use React Query for Automatic Retries
const { data } = useQuery ({
queryKey: [ 'stats' ],
queryFn: api . getStats ,
retry : ( failureCount , error ) => {
// Don't retry on client errors (4xx)
if ( error . statusCode >= 400 && error . statusCode < 500 ) {
return false ;
}
// Retry server errors (5xx) up to 3 times
return failureCount < 3 ;
},
retryDelay : ( attemptIndex ) => Math . min ( 1000 * 2 ** attemptIndex , 30000 )
});
6. Validate Data Before Sending
function trackInteraction ( nombre : string , accion : string ) {
// Client-side validation
if ( ! nombre || ! accion ) {
showErrorToast ( 'Component name and action are required' );
return ;
}
if ( nombre . length > 50 ) {
showErrorToast ( 'Component name is too long' );
return ;
}
// Make API call
return api . trackInteraction ({ nombre , accion , tipo_usuario: 'anonymous' });
}
Client Setup Learn how to configure the API client and make requests
Authentication Understand JWT authentication and protected endpoints