المسارات غير المتزامنة
جزء من قسم أساسيات Express في رحلة Node.js على Coddy. الدرس 18 من 20.
تختفي المصفوفة عند إعادة تشغيل الخادم. أما الملف فيحتفظ بالبيانات.
import { readFile } from 'node:fs/promises';
app.get('/api/forecast', async (req, res) => {
const text = await readFile('data/forecast.json', 'utf8');
res.json(JSON.parse(text));
});يمكن أن يكون المسار دالة async تنتظر باستخدام await، وهنا تنتظر readFile من node:fs/promises. يراقب Express 5 الوعد الذي تعيده الدالة: إذا رُفض، بسبب فقدان الملف أو تلف JSON فيه، ينتقل الخطأ إلى معالج الأخطاء لديك، كما يحدث عند طرح استثناء.
من دون await، ستحصل على وعد بدلًا من البيانات، وسيُرسل res.json(promise) القيمة {}. يحتوي الملف الجديد data/recipes.json على الوصفات الأربع.
التحدي
سهل- يقرأ كل مسار في
routes/recipes.jsالوصفات منdata/recipes.jsonعند كل طلب. - يحفظ
POSTالقائمة الجديدة مرة أخرى باستخدامwriteFile.
أرسل وصفة، ثم أرسل GET /api/recipes وGET /api/recipes/5 وGET /api/recipes/99.
جرّب بنفسك
import express from 'express';
import { recipes } from '../data.js';
const router = express.Router();
// كل وسم مرة واحدة، بترتيب أبجدي.
router.get('/tags', (req, res) => {
const tags = new Set(recipes.flatMap((recipe) => recipe.tags));
res.json([...tags].sort());
});
router.get('/', (req, res) => {
res.json(recipes);
});
router.get('/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
res.json(recipe);
});
router.post('/', (req, res) => {
const { title, tags } = req.body;
if (!title) {
res.status(400).json({ error: 'A recipe needs a title' });
return;
}
const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
recipes.push(recipe);
res.status(201).json(recipe);
});
router.get('/:id/scale', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
const by = Number(req.query.by);
if (!Number.isFinite(by) || by <= 0) {
res.status(400).json({ error: 'Scale by a positive number' });
return;
}
if (!recipe.servings) {
throw new Error(`Recipe ${recipe.id} has no servings`);
}
res.json({ title: recipe.title, servings: recipe.servings * by });
});
export default router;
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.