Error Handling
The Tanqueo Backend API uses consistent error handling patterns across all endpoints with standardized error responses and HTTP status codes.
All errors return a JSON object with an error field:
{
"error" : "Descriptive error message"
}
Example Error Response
curl http://localhost:5000/api/tanqueos \
-H "Authorization: Bearer invalid-token"
Response (401):
{
"error" : "Token inválido"
}
HTTP Status Codes
The API uses standard HTTP status codes to indicate success or failure:
Success Codes
Code Meaning Usage 200 OK Successful GET, PUT, DELETE 201 Created Successful POST (resource created)
Client Error Codes (4xx)
Code Meaning Common Scenarios 400 Bad Request Missing required fields, validation errors, malformed data 401 Unauthorized Missing or invalid authentication token 404 Not Found Resource doesn’t exist, route not found
Server Error Codes (5xx)
Code Meaning Common Scenarios 500 Internal Server Error Unexpected errors, database connection issues
Common Error Scenarios
Authentication Errors
Missing Token
Request:
curl http://localhost:5000/api/tanqueos
Response (401):
{
"error" : "Token no proporcionado"
}
Invalid Token
Request:
curl http://localhost:5000/api/tanqueos \
-H "Authorization: Bearer expired-or-invalid-token"
Response (401):
{
"error" : "Token inválido"
}
User Not Found
Token is valid but user doesn’t exist in usuarios table:
Response (401):
{
"error" : "Usuario no encontrado en la base de datos"
}
Login Errors
Missing Credentials
Request:
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "[email protected] "}'
Response (400):
{
"error" : "Email y contraseña son requeridos"
}
Invalid Credentials
Request:
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected] ",
"password": "wrong-password"
}'
Response (400):
{
"error" : "Invalid login credentials"
}
The exact error message comes from Supabase Auth and may vary. Common messages include “Invalid login credentials” or “Email not confirmed”.
Refresh Token Errors
Missing Refresh Token
Request:
curl -X POST http://localhost:5000/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{}'
Response (400):
{
"error" : "Refresh token es requerido"
}
Invalid or Expired Refresh Token
Response (401):
{
"error" : "Refresh token inválido o expirado"
}
Resource Not Found
Route Not Found
Request:
curl http://localhost:5000/api/nonexistent
Response (404):
{
"error" : "Ruta no encontrada"
}
Record Not Found
Request:
curl http://localhost:5000/api/tanqueos/99999 \
-H "Authorization: Bearer valid-token"
Response (404):
{
"error" : "Tanqueo no encontrado"
}
Database Errors
Query Error
When a database query fails (constraint violation, type mismatch, etc.):
Response (400):
{
"error" : "duplicate key value violates unique constraint \" unique_placa \" "
}
Database error messages are passed through from Supabase/PostgreSQL. They provide detailed information for debugging.
Server Errors
Internal Server Error
Unexpected errors that weren’t handled explicitly:
Response (500):
{
"error" : "Error en el servidor"
}
Error Handling Patterns
Controller Pattern
All controllers follow this consistent error handling pattern:
src/controllers/tanqueos.controller.ts
export const tanqueosController = {
async getById ( req : AuthRequest , res : Response ) : Promise < void > {
try {
const { id } = req . params ;
// Database query
const { data , error } = await req . supabase
?. from ( 'tanqueos' )
. select ( '*' )
. eq ( 'id' , id )
. single () || await supabase
. from ( 'tanqueos' )
. select ( '*' )
. eq ( 'id' , id )
. single ();
// Handle query errors (400)
if ( error ) {
console . error ( 'Error en getById:' , error );
res . status ( 400 ). json ({ error: error . message });
return ;
}
// Handle not found (404)
if ( ! data ) {
res . status ( 404 ). json ({ error: 'Tanqueo no encontrado' });
return ;
}
// Success (200)
res . json ( data );
} catch ( error ) {
// Unexpected errors (500)
console . error ( 'Error en getById:' , error );
res . status ( 500 ). json ({ error: 'Error en el servidor' });
}
}
};
Three-Layer Error Handling
Database Error Handling
Check the error returned from Supabase queries: const { data , error } = await supabase . from ( 'table' ). select ();
if ( error ) {
res . status ( 400 ). json ({ error: error . message });
return ;
}
Business Logic Validation
Validate data and business rules: if ( ! data || data . length === 0 ) {
res . status ( 404 ). json ({ error: 'No se encontraron registros' });
return ;
}
if ( ! req . body . fecha || ! req . body . conductor_id ) {
res . status ( 400 ). json ({ error: 'Campos requeridos faltantes' });
return ;
}
Global Exception Handler
Catch unexpected errors with try-catch: try {
// All logic here
} catch ( error ) {
console . error ( 'Error:' , error );
res . status ( 500 ). json ({ error: 'Error en el servidor' });
}
Authentication Middleware Errors
The auth middleware handles errors before reaching controllers:
src/middleware/auth.middleware.ts
export async function authMiddleware (
req : AuthRequest ,
res : Response ,
next : NextFunction
) : Promise < void > {
try {
// Extract token
const token = req . headers . authorization ?. replace ( 'Bearer ' , '' );
if ( ! token ) {
res . status ( 401 ). json ({ error: 'Token no proporcionado' });
return ;
}
// Verify with Supabase
const { data : { user }, error } = await supabase . auth . getUser ( token );
if ( error || ! user ) {
res . status ( 401 ). json ({ error: 'Token inválido' });
return ;
}
// Fetch user data
const { data : userData , error : userError } = await supabase
. from ( 'usuarios' )
. select ( '*' )
. eq ( 'id' , user . id )
. single ();
if ( userError || ! userData ) {
res . status ( 401 ). json ({ error: 'Usuario no encontrado en la base de datos' });
return ;
}
// Attach to request and continue
req . user = userData ;
req . accessToken = token ;
req . supabase = createAuthClient ( token );
next ();
} catch ( error ) {
console . error ( 'Error en auth middleware:' , error );
res . status ( 401 ). json ({ error: 'Error de autenticación' });
}
}
Error Logging
All errors are logged to the console for debugging:
console . error ( 'Error en getAll:' , error );
Console Output:
Error en getAll: Error: relation "tanqueos" does not exist
at Object.from (/app/node_modules/@supabase/postgrest-js/dist/index.js:...)
In production, consider using a structured logging service (like Winston, Pino, or cloud logging) instead of console.error for better error tracking and alerting.
Client-Side Error Handling
Recommended Client Pattern
try {
const response = await fetch ( 'http://localhost:5000/api/tanqueos' , {
headers: {
'Authorization' : `Bearer ${ accessToken } ` ,
'Content-Type' : 'application/json'
}
});
if ( ! response . ok ) {
const errorData = await response . json ();
throw new Error ( errorData . error || 'Unknown error' );
}
const data = await response . json ();
// Handle success
} catch ( error ) {
// Handle error
if ( error . message === 'Token inválido' ) {
// Redirect to login or refresh token
} else {
// Show error to user
console . error ( 'API Error:' , error );
}
}
Handling Token Expiration
async function makeAuthenticatedRequest ( url : string , options : RequestInit ) {
let response = await fetch ( url , {
... options ,
headers: {
... options . headers ,
'Authorization' : `Bearer ${ accessToken } `
}
});
// Token expired - try refresh
if ( response . status === 401 ) {
const refreshed = await refreshAccessToken ();
if ( refreshed ) {
// Retry with new token
response = await fetch ( url , {
... options ,
headers: {
... options . headers ,
'Authorization' : `Bearer ${ newAccessToken } `
}
});
} else {
// Refresh failed - redirect to login
window . location . href = '/login' ;
}
}
return response ;
}
Error Prevention Best Practices
Validate Input Check required fields and data types before processing
Use TypeScript Leverage type checking to catch errors at compile time
Handle Nulls Check for null/undefined before accessing properties
Test Edge Cases Test with invalid data, missing fields, and edge cases
async create ( req : AuthRequest , res : Response ): Promise < void > {
try {
const { fecha , conductor_id , placa_id , bomba_id , area_operacion_id } = req . body ;
// Validate required fields
if ( ! fecha || ! conductor_id || ! placa_id || ! bomba_id || ! area_operacion_id ) {
res . status ( 400 ). json ({
error: 'Campos requeridos: fecha, conductor_id, placa_id, bomba_id, area_operacion_id'
});
return ;
}
// Validate data types
if ( typeof conductor_id !== 'number' || conductor_id <= 0 ) {
res . status ( 400 ). json ({ error: 'conductor_id debe ser un número positivo' });
return ;
}
// Validate date format
if ( ! / ^ \d {4} - \d {2} - \d {2} $ / . test ( fecha )) {
res . status ( 400 ). json ({ error: 'fecha debe tener formato YYYY-MM-DD' });
return ;
}
// Proceed with creation
const { data , error } = await req . supabase
?. from ( 'tanqueos' )
. insert ( req . body )
. select ()
. single ();
// ... rest of handler
} catch (error) {
console. error ( 'Error en create:' , error );
res.status( 500 ).json ({ error : 'Error en el servidor' });
}
}
Summary
Scenario Status Code Error Message Pattern Missing auth token 401 Token no proporcionadoInvalid auth token 401 Token inválidoMissing required fields 400 [Field] es requeridoDatabase query error 400 Database error message Resource not found 404 [Resource] no encontradoRoute not found 404 Ruta no encontradaUnexpected error 500 Error en el servidor
All error responses include detailed error messages to help developers debug issues. In production, consider sanitizing database error messages to avoid exposing sensitive schema information.