میخوام براتون در مورد بهترین روش طراحی API توضیح بدم و راهکار هایی رو بنویسم که باعث میشه استفاده از API که طراحی میکنی برای بقیه خیلی ساده باشه .
خب همون طور که میدونید REST API خیلی جاها استفاده میشه و خب من همیشه یکی از دغدغه هام این بوده که REST API رو یه طوری طراحی کنم که استفاده ازش خیلی خیلی ساده باشه و همزمان عملکرش تحت تاثیر قرار نگیره .
باور کنید دیگه زمان استفاه از soap و امثالهم سر اومده و واقعا استفاده از XML نفرت انگیزه و نهایتا منجر به سردرد و اعصاب خوردی ( و شایدم دعوا ) با فرانت دولوپر میشه پس به عنوان قدم اول از JSON استفاده کنید .
یک مورد مهم دیگه Form data برای انتقال دیتا خوبه به خوصوص برای فایل ولی بهتره که برای ارسال متن از form data استفاده نکنید و همون از JSON استفاده کنید.
اجازه بدید واضح تر توضیح بدم ما نباید از یک فعل داخل endpoint هایی که داریم استفاده کنیم مثلا ننویسیم getArticleList حالا چرا ؟ خب چون داخل method HTTP ی که داریم فعل استفاه شده و استفاه مجدد از یک فعل فقط باعث طولانی شدن اون اند پوینت میشه و هیچ کمکی دیگه ای نمیکنه .
پس عملی که اون ریکوئست انجام میده رو نباید داخل اندپوینتش ذکر کنیم و باید با HTTP method هایی که معموله مثل GET, POST, PUT, DELETE عملکر اون ریکوئست رو نشون بدیم.
خب پس فعلا با ۲ تا قانونی که بالا توضیح دادم اگه بخوایم route هایی برای یک سری مقاله درست کنیم اینطوری میشه :
برای گرفتن لیست مقاله ها :
app.get('/articles', (req, res) => { const articles = []; // code to retrieve an article... res.json(articles); });
برای ایجاد مقاله جدید :
app.post('/articles', (req, res) => { // code to add a new article... res.json(req.body); });
برای ادیت کردن یه مقاله :
app.put('/articles/:id', (req, res) => { const { id } = req.params; // code to update an article... res.json(req.body); });
و در آخر برای حذف یک مقاله :
app.delete('/articles/:id', (req, res) => { const { id } = req.params; // code to delete an article... res.json({ deleted: id }); });
خب دیدید ما از هیچ فعلی برای تعریف مسیر هامون استفاه نکردیم
تو همین مثال قبلی /articles/
قسمتی از endpoint ما بود که خب به صورت جمع نوشته شده و دیگه نیازی نیست برای گرفتن لیست مقاله ها یه چیز مضخرفی مثل getArticleListForAllUser اضافه کنیم :)
ها این که وگفتی یعنی چه ! مثلا فرض کنید داخل مثال قبلی مون هر مقاله یک لیستی از کامنت ها داره و ما میخوایم این کامنت هارو به یوزر نشون بدیم خب برای انجام این کار باید /comments
رو به آخر articles/
اضافه کنیم که اگه بخوام با کد نشون بدم اینطوری میشه :
app.get('/articles/:articleId/comments', (req, res) => { const { articleId } = req.params; const comments = []; // code to get comments by articleId res.json(comments); });
ما اگه این کارو انجام بدیم بقیه خیلی راحت متوجه میشن که کامنت های ما زیر مجموعه ساختار مقاله هامون قرار داره و دیگه نیست کلی توضیح بدید این موضوع رو .
خیلی از مواقع دیتابیس ما خیلی بزرگ میشه و ما نباید همه دیتا رو یکباره برای کاربر بفرستیم برای همین ما باید به کاربر اجازه فیلتر کردن دیتا رو بدیم صفحه بندی و فیلتر کردن پرفورمنس رو خیلی بالا میبره و خب زمانی که مقادیر دیتا زیاد میشه اهمیت این موضوع خیلی برجسته تر میشه اگه بخوام فیلتر کردن رو با یک مثال نشون بدم اینطوری میشه :
// employees data in a database const employees = [ { firstName: 'Jane', lastName: 'Smith', age: 20 }, //... { firstName: 'John', lastName: 'Smith', age: 30 }, { firstName: 'Mary', lastName: 'Green', age: 50 }, ] app.use(bodyParser.json()); app.get('/employees', (req, res) => { const { firstName, lastName, age } = req.query; let results = [...employees]; if (firstName) { results = results.filter(r => r.firstName === firstName); } if (lastName) { results = results.filter(r => r.lastName === lastName); } if (age) { results = results.filter(r => +r.age === +age); } res.json(results); });
ما میتونیم با کش کردن دیتا روی memory کاری کنیم که هر بار برای ارسال دیتا به یوزر نیاز به query زدن نباشه .
راه حل های زیادی برای انجام این کار وجود داره مثلا استفاده از Redis و امثالهم ولی خب اگه حوصله انجام این کارا رو ندارید حداقل میتونید با apicache middleware خیلی راحت و بدون نیاز به کانفیگ خاصی این کارو انجام بدید :
const express = require('express'); const bodyParser = require('body-parser'); const apicache = require('apicache'); const app = express(); let cache = apicache.middleware; app.use(cache('5 minutes')); // employees data in a database const employees = [ { firstName: 'Jane', lastName: 'Smith', age: 20 }, //... { firstName: 'John', lastName: 'Smith', age: 30 }, { firstName: 'Mary', lastName: 'Green', age: 50 }, ] app.use(bodyParser.json()); app.get('/employees', (req, res) => { res.json(employees); }); app.listen(3000, () => console.log('server started'));
برای این که کلاینت های مختلف ما به مشکل نخورن باید api های ما ورژن بندی مناسبی دارشته باشه مثلا میتونیم از روش semantic version که خیلی این روزا استفاده میشه استفاه کنیم.
با این روش اون قدیمی ها کم کم خارج میشن و دیگه نیازی نیست همه رو مجبور به استفاه از یک api واحد کنیم و مثلا api v1 ما برای کسایی میشه که نمیخوان تغییر کنن ولی به v2 هامون فیچر های جدید ارائه میدیم و البته اهمیت این موضوع زمانی آشکار میشه که api های ما public باشه و همه بتونن ازش استفاده کنند.
راستی این اولین نوشته من داخل ویرگول بود خوشحال میشم نظرتون رو بگید :)