Conflictos
Parte de la sección API REST del Journey de Node.js de Coddy. Lección 9 de 18.
Una segunda tarea llamada "Send the invoice" hace que la lista resulte confusa, pero su cuerpo es válido. Eso es un 409 Conflict: la solicitud entra en conflicto con lo que ya está almacenado. Un 400 significa que el cuerpo en sí es incorrecto.
const taken = users.some((u) => u.name.toLowerCase() === name.toLowerCase());
if (taken) {
return sendError(res, 409, 'conflict', 'That username is taken');
}Compara en minúsculas, ya que la gente interpreta "Ada" y "ada" como el mismo nombre. Cuando una solicitud cambia el nombre de algo, omite el propio elemento en la búsqueda. De lo contrario, un usuario que guarda el formulario sin cambiar su nombre entra en conflicto consigo mismo.
Desafío
Fácil- Un título que ya tiene otra tarea, sin importar las mayúsculas o minúsculas, responde con el código 409 y el código
conflict: en POST, y en PATCH y PUT cuando establecen un título. - Una tarea puede conservar su propio título.
Envía POST con { "title": "book the meeting room" }, después PUT /api/tasks/2 con { "title": "Book the meeting room", "done": true }.
Pruébalo tú mismo
import { Router } from 'express';
import { z } from 'zod';
import { sendError, sendInvalid } from '../errors.js';
import { nextId, tasks } from '../store.js';
const router = Router();
// Una tarea completa, tal como la envía PUT. .strict() rechaza los campos que no figuran en su lista.
const Task = z
.object({
title: z.string().trim().min(1).max(100),
done: z.boolean(),
})
.strict();
const NewTask = Task.partial({ done: true });
const TaskChanges = Task.partial();
router.get('/', (req, res) => {
res.json(tasks);
});
router.get('/:id', (req, res) => {
const task = tasks.find((t) => t.id === Number(req.params.id));
if (!task) {
return sendError(res, 404, 'not_found', 'Task not found');
}
res.json(task);
});
router.post('/', (req, res) => {
const result = NewTask.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
const { title, done = false } = result.data;
const task = { id: nextId(), title, done, createdAt: new Date().toISOString() };
tasks.push(task);
res.status(201).location(`/api/tasks/${task.id}`).json(task);
});
router.patch('/:id', (req, res) => {
const task = tasks.find((t) => t.id === Number(req.params.id));
if (!task) {
return sendError(res, 404, 'not_found', 'Task not found');
}
const result = TaskChanges.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
Object.assign(task, result.data);
res.json(task);
});
router.put('/:id', (req, res) => {
const index = tasks.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return sendError(res, 404, 'not_found', 'Task not found');
}
const result = Task.safeParse(req.body);
if (!result.success) {
return sendInvalid(res, result.error);
}
const { id, createdAt } = tasks[index];
tasks[index] = { id, ...result.data, createdAt };
res.json(tasks[index]);
});
router.delete('/:id', (req, res) => {
const index = tasks.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return sendError(res, 404, 'not_found', 'Task not found');
}
tasks.splice(index, 1);
res.status(204).end();
});
export default router;