express-endpoint
SKILL.md
Express Endpoint Skill
Crea un nuevo endpoint Express siguiendo el patron del proyecto Ocralis.
When to Activate
- Crear un nuevo endpoint REST en el backend
- Agregar operaciones CRUD para un recurso
- Integrar un nuevo modulo con validacion y autenticacion
- Extender un controller existente con nuevos handlers
Patron Completo
1. Controller (controllers/{modulo}.controller.js)
import { prisma } from "../prismaClient.js";
import logger from "../utils/logger.js";
import { z } from "zod";
// Schema de validacion Zod
const createSchema = z.object({
nombre: z.string().min(1, "Nombre requerido"),
email: z.string().email("Email invalido"),
// campos con tipos y validaciones segun el recurso
});
export const create = async (req, res) => {
try {
// 1. Validar input con Zod
const validated = createSchema.parse(req.body);
// 2. Obtener usuario autenticado
const usuario = req.user;
// 3. Logica de negocio (llamar servicio si es externo)
const result = await prisma.model.create({
data: { ...validated, usuarioId: usuario.id },
});
// 4. Respuesta consistente
res.status(201).json({ success: true, data: result });
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
success: false,
error: "Datos invalidos",
details: error.errors,
});
}
logger.error(error, "Error en create");
res.status(500).json({ success: false, error: "Error interno del servidor" });
}
};
export const list = async (req, res) => {
try {
const usuario = req.user;
const items = await prisma.model.findMany({
where: { usuarioId: usuario.id },
orderBy: { createdAt: "desc" },
});
res.json({ success: true, data: items });
} catch (error) {
logger.error(error, "Error en list");
res.status(500).json({ success: false, error: "Error interno del servidor" });
}
};
export const getById = async (req, res) => {
try {
const { id } = req.params;
const item = await prisma.model.findUnique({
where: { id: parseInt(id) },
});
if (!item) {
return res.status(404).json({ success: false, error: "No encontrado" });
}
res.json({ success: true, data: item });
} catch (error) {
logger.error(error, "Error en getById");
res.status(500).json({ success: false, error: "Error interno del servidor" });
}
};
export const update = async (req, res) => {
try {
const { id } = req.params;
const validated = updateSchema.parse(req.body);
const item = await prisma.model.update({
where: { id: parseInt(id) },
data: validated,
});
res.json({ success: true, data: item });
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
success: false,
error: "Datos invalidos",
details: error.errors,
});
}
logger.error(error, "Error en update");
res.status(500).json({ success: false, error: "Error interno del servidor" });
}
};
export const remove = async (req, res) => {
try {
const { id } = req.params;
await prisma.model.delete({ where: { id: parseInt(id) } });
res.json({ success: true, data: { message: "Eliminado correctamente" } });
} catch (error) {
logger.error(error, "Error en remove");
res.status(500).json({ success: false, error: "Error interno del servidor" });
}
};
2. Routes (routes/{modulo}.routes.js)
import express from "express";
import { create, list, getById, update, remove } from "../controllers/{modulo}.controller.js";
import { authenticateToken } from "../middleware/auth.middleware.js";
const router = express.Router();
// Aplicar auth a todas las rutas del modulo
router.use(authenticateToken);
router.post("/", create);
router.get("/", list);
router.get("/:id", getById);
router.put("/:id", update);
router.delete("/:id", remove);
export default router;
3. Registrar en app.js
import moduloRoutes from "./routes/{modulo}.routes.js";
app.use("/api/{modulo}", moduloRoutes);
4. Service externo (opcional) (services/{modulo}.service.js)
Solo crear si el endpoint necesita integrar con APIs externas (SALTRA, GCS, Gemini, etc.). Para operaciones CRUD puras con Prisma, la logica va directamente en el controller.
Convenciones del Proyecto
| Concepto | Convencion |
|---|---|
| Modulos | ES Modules (import/export, no require) |
| Prisma | import { prisma } from "../prismaClient.js" |
| Auth | req.user contiene el usuario autenticado (set por authenticateToken) |
| Respuestas | { success: true/false, data: ..., error: "mensaje" } |
| Logging | Pino: logger.info, logger.error, logger.debug |
| Validacion | Zod inline en el controller |
| IDs | Integer autoincrement en Prisma |
| Errores Zod | Status 400 con details: error.errors |
| Errores 404 | { success: false, error: "No encontrado" } |
| Errores 500 | { success: false, error: "Error interno del servidor" } |
Checklist
- Controller creado con try/catch y logging en cada handler
- Schema Zod para validacion de input (create y update)
- Routes con
authenticateTokenmiddleware - Registrado en
app.jscon prefijo/api/{modulo} - Service creado si hay integracion externa
- Respuestas consistentes (
success/data/error) - IDs parseados con
parseInt()en params - 404 para recursos no encontrados
- Actualizar README.md y README_CONTROLLERS.md
Weekly Installs
1
Repository
davidcastagnetoa/skillsFirst Seen
6 days ago
Security Audits
Installed on
amp1
cline1
trae1
trae-cn1
opencode1
cursor1