Menu
CoddyTech

התנגשויות

חלק מהיחידה REST APIs במסלול ה-Node.js של Coddy. שיעור 9 מתוך 18.

משימה שנייה בשם "שליחת החשבונית" מבלבלת את הרשימה, אבל גוף הבקשה שלה תקין. זהו 409 Conflict: הבקשה מתנגשת עם מה שכבר שמור. קוד 400 פירושו שגוף הבקשה עצמו שגוי.

const taken = users.some((u) => u.name.toLowerCase() === name.toLowerCase());
if (taken) {
    return sendError(res, 409, 'conflict', 'That username is taken');
}

השוו באותיות קטנות, כי אנשים קוראים את "Ada" ואת "ada" כאותו שם. כשבקשה משנה את שם הדבר, דלגו על הפריט עצמו בחיפוש. אחרת, משתמש ששומר את הטופס בלי לשנות את שמו יקבל התנגשות עם עצמו.

challenge icon

אתגר

קל
  • כותרת שכבר קיימת במשימה אחרת, ללא תלות באותיות גדולות או קטנות, מחזירה 409 עם קוד conflict: בבקשת POST, וגם בבקשות PATCH ו-PUT כשהן מגדירות כותרת.
  • משימה יכולה לשמור על הכותרת שלה.

שלחו בקשת POST עם { "title": "book the meeting room" }, ואז PUT /api/tasks/2 עם { "title": "Book the meeting room", "done": true }.

נסו בעצמכם

import { Router } from 'express';
import { z } from 'zod';
import { sendError, sendInvalid } from '../errors.js';
import { nextId, tasks } from '../store.js';

const router = Router();

// משימה שלמה, כפי ש-PUT שולח אותה. ‎.strict()‎ דוחה שדות שאינם מופיעים ברשימה שלו.
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;

כל השיעורים ביחידה REST APIs