Guías
API RESTful

Guía para Construir un API RESTful

Flujo paso a paso para exponer un API REST consistente con Fox Framework.

Contenido

Estructura del Proyecto

src/
  controllers/
    user.controller.ts
  services/
    user.service.ts
  routes/
    user.routes.ts
  middleware/
    auth.middleware.ts
  validation/
    user.schema.ts

Modelos y DTOs

// src/dto/user.dto.ts
export interface CreateUserDTO { name:string; email:string; password:string; }
export interface UserResponseDTO { id:string; name:string; email:string; createdAt:string; }

Servicios (Business Logic)

// src/services/user.service.ts
export class UserService {
  constructor(private users: UserRepository) {}
  async create(data: CreateUserDTO): Promise<UserResponseDTO> {
    // hash password, validaciones extra
    const entity = await this.users.create({ ...data, passwordHash: 'hashed' });
    return this.toDTO(entity);
  }
  async find(id:string){ const u = await this.users.findById(id); return u? this.toDTO(u): null; }
  private toDTO(u:any): UserResponseDTO { return { id:u.id, name:u.name, email:u.email, createdAt:u.created_at }; }
}

Controladores

// src/controllers/user.controller.ts
import { Request, Response } from 'fox-framework';
export class UserController {
  constructor(private service: UserService) {}
  create = async (req:Request,res:Response) => {
    const dto = await this.service.create(req.body);
    res.status(201).json({ success:true, data:dto });
  };
  getById = async (req:Request,res:Response) => {
    const user = await this.service.find(req.params.id);
    if(!user) return res.status(404).json({ success:false, message:'User not found' });
    res.json({ success:true, data:user });
  };
}

Rutas y Middlewares

// src/routes/user.routes.ts
import { Router } from 'fox-framework';
const router = Router.create();
const controller = new UserController(userService);
router.post('/users', validate(createUserSchema), controller.create);
router.get('/users/:id', controller.getById);
export default router;

Registro en servidor:

app.use('/api/v1', userRoutes);

Validación

// src/validation/user.schema.ts
import { z } from 'zod';
export const createUserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  password: z.string().min(8)
});
 
export function validate(schema){
  return (req,res,next)=> {
    const result = schema.safeParse(req.body);
    if(!result.success){
      return res.status(422).json({ success:false, errors: result.error.issues });
    }
    req.body = result.data; next();
  };
}

Errores y Respuestas

Middleware de error uniforme:

app.use((err,req,res,next)=> {
  const status = err.status || 500;
  res.status(status).json({ success:false, message: err.message, code: err.code });
});

Formato estándar de respuesta:

{ "success": true, "data": { ... } }
{ "success": false, "message": "..." }

Versionado de API

Estrategias:

  • Prefijo path (/api/v1) recomendado
  • Cambios breaking -> nueva versión (/api/v2)
  • Mantener al menos dos versiones activas durante migración

Routing multi-versión:

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

Mejores Prácticas

  • Stateless (no almacenar sesión server-side salvo tokens revocados)
  • Idempotencia en PUT/PATCH
  • Usar códigos HTTP correctos (201 en creaciones)
  • Paginación consistente (?cursor= o ?page=)
  • Documentar (OpenAPI) y validar contratos

Volver al índice de guías