چطور یک API ساده بسازیم؟

آموزش نوشتن یک API ساده با Node.js
آموزش نوشتن یک API ساده با Node.js

از سال ۲۰۰۹ تا به امروز Node.js همواره در حال توسعه و پیشرفت بوده است و توسعه‌دهنده‌ها در سراسر دنیا کتابخانه‌ها و پکیج‌های ثالث زیادی را برای آن توسعه داده‌اند. به دلیل وجود قابلیت اجرای دستورات سمت سرور، Node.js به طور گسترده‌ای برای پیاده‌سازی REST APIها مورد استفاده قرار می‌گیرد.

در این پست قرار است با یکدیگر یک API ساده را با استفاده از Node.js بسازیم.

توسعه رابط کاربری REST API

توسعه رابط کاربری API شامل نوشتن یک منطق در سمت بک‌اند برای رسیدی به درخواست‌های API می‌شود. این فرایند شبیه چرخه توسعه نرم‌افزار است. در این چرخه طراحی در سطوح بالا شروع شده و پیاده‌سازی کد در سطوح پایین صورت می‌گیرد. در طی مسیر توسعه، باید به نکاتی نظیر انتخاب بهترین الگوریتم، ابزارهای ثالث، پایگاه داده و کتابخانه‌ها توجه کنیم.

از این اینجا به بعد کار را در قالب یک مثال ادامه می‌دهیم تا درک مسئله راحت‌تر باشد.

یک سرویس نظارت بر IP (مانیتورینگ IP) را در نظر بگیرید. وظیفه این سرویس رهگیری گروهی از IPها بر اساس موقعیت جغرافیایی آن‌ها است. REST APIهای مورد استفاده در این سرویس حول مدیریت آدرس‌های IP طراحی شده‌اند. بنابراین ما می‌توانیم رابط‌های کاربری برای سرویس نظارت بر IP را به شکل زیر تعریف کنیم:

رابط‌های کاربری برای IPMon
رابط‌های کاربری برای IPMon

انتخاب اجزای بک‌اند API

برای هر IP که توسط سرویس رهگیری می‌شود، باید محلی برای ذخیره اطلاعات آن وجود داشته باشد. انتخاب یک پایگاه داده مناسب برای سرویس بسیار مهم است. در صورتی که در این زمینه اطلاعات کافی ندارید (هرچند که خیلی بعید به نظر می‌رسد)، حتما از کمک یک متخصص بهره ببرید. در قسمت‌های بعدی که مراحل توسعه را شرح می‌دهیم، درباره این موضوع بیشتر صحبت می‌کنیم.

از سوی دیگر برای دریافت اطلاعات مربوط به یک IP باید از یک سرویس ثالث استفاده کنیم. IPها به طور عمومی توسط سازمان‌های بزرگ تخصیص داده شده و پیگیری می‌شوند، و به همین دلیل بهتر است از سرویس‌های آن‌ها برای دریافت این اطلاعات بهره ببریم. این سرویس‌ها را می‌توانیم از طریق یک API در بک‌اند در سرویس خودمان پیاده‌سازی کنیم.

برای این مثال، ما از IP Geolocation API که به صورت رایگان در وبسایت RapidAPI قرار دارد، استفاده می‌کنیم. برای این کار کافی است یک اکانت رایگان RapidAPI بسازید و این API را دریافت کنید.

پیاده‌سازی رابط کاربری API با استفاده از Node.js

حالا به قسمت قشنگ این پست رسیده‌ایم، نوشتن کد API. قبل از شروع به نوشتن کد از داشتن پیش‌نیازهای لازم برای نوشتن و تست محلی API اطمینان حاصل کنید:

Node.js Runtime: این ران‌تایم را می‌توانید از سایت Node.js دانلود و نصب کنید. پیشنهاد ما نسخه LTS است.

cURL: ابزار curl را می‌توانید در وبسایت آن پیدا کنید. این ابزار برای تست رابط کاربری API استفاده می‌شود.

مراحل توسعه API

ویرایشگر کد خودتان را اجرا کرده و مراحل زیر را برای توسعه API انجام دهید. برای راحتی کار، ما اسم این API را IPMon گذاشته‌ایم، اما شما می‌توانید از هر اسمی که دوست دارید استفاده کنید. (دقت داشته باشید اسم انتخابی خود را در کد به کار ببرید).

مرحله اول: ایجاد یک پروژه Node.js

یک دایرکتوری سطح بالا بسازید:

mkdir node-api

در اینجا node-api دایرکتوری سطح بالای شماست که همه فایل‌های پروژه را در خود نگه می‌دارد. به این پوشه بروید و یک پروژه Node.js ایجاد کنید:

cd node-api
npm init -y

در ادامه یک فایل جدید با نام server.js بسازید:

touch server.js

مرحله دوم: اضافه کردن اندپوینت‌های HTTP

فایل server.js حاوی کد اصلی رابط کاربری API ما است. در ابتدا باید ماژول‌های اصلی را اضافه کنید:

const http = require ('http'); // برای ایجاد سرور
const url = require('url'); 
const https = require('https');// https برای ارسال درخواست‌های
 
var ip_table = new Map();//

در قسمت بعد شما باید اندپوینت‌های سرور HTTP را برای رسیدگی به درخواست‌های API ایجاد کنید:

// create basic server and implement handling different requests
const app = http.createServer( async (req, res) => {
 // parse the incoming url
 const parsedURL = url.parse(req.url, true);
 
 //check for  POST /api/ipmon/ip
 if ( parsedURL.pathname === '/api/ipmon/ip' && req.method === 'POST') {
 // check if ip is prsent in query else send error
 if(!parsedURL.query.ip) {
                        res.statusCode = 400;
                        res.end(&quotIP address is required, please add IP address in the query&quot);
                    } else {
                        handleCreate(parsedURL.query.ip,res);
                    }
 
                }
 //check for GET /api/ipmon/ip/show 
 else if (parsedURL.pathname === '/api/ipmon/ip/show' && req.method === 'GET'){
                    handleShow(res);              
                }
 //check for  PUT /api/ipmon/ip/{ip_address}
 else if (parsedURL.pathname.startsWith('/api/ipmon/ip') && req.method === 'PUT'){
 
                    var ipAddr = extractIPAddress(parsedURL.pathname)
 
 if(!ipAddr.length) {
                        res.statusCode = 400;
                        res.end(&quotInvalid IP Address&quot);
                    } else {
                        handleUpdate(ipAddr,res);
                    }
                }
 //check for  DELETE /api/ipmon/ip/{ip_address}
 else if (parsedURL.pathname.startsWith('/api/ipmon/ip') && req.method === 'DELETE'){
 
                    var ipAddr = extractIPAddress(parsedURL.pathname)
 
 if(!ipAddr.length) {
 
                        res.statusCode = 400;
                        res.end(&quotInvalid IP Address&quot);
                    } else {
                        handleDelete(ipAddr,res);
                    }
                }
 //check for GET /api/ipmon/ip/{ip_address}
 else if (parsedURL.pathname.startsWith('/api/ipmon/ip') && req.method === 'GET'){
                    var ipAddr = extractIPAddress(parsedURL.pathname)
 
 if(!ipAddr.length) {
                        res.statusCode = 400;
                        res.end(&quotInvalid IP Address&quot);
                    } else {
                        handleRead(ipAddr,res);
                    }
                } 
 // if url doent match any send error
 else {
                    res.statusCode = 400;
                    res.end(&quotAPI Endpoint Not Supported&quot);
                }
        });//End of create server.
app.listen(4000);

این کد به Node.js می‌گوید که به درخواست‌ها روی پورت ۴۰۰۰ گوش کند. اندپوینت‌های API مطابق جدول بالا تعیین شده‌اند.

مرحله سوم: اضافه کردن هندلرهای اندپوینت API

فانکشن‌های هندلر منطق‌های کسب‌‌و‌کاری را پیاده‌سازی می‌کنند. در ادامه هندلرها را یک‌به‌یک پیاده‌سازی می‌کنیم. در فایل server.js فانکشن ( )handleCreate را برای رسیدگی به درخواست‌های /api/ipmon/ip ایجاد می‌کنیم.

/* function to handle create */
function handleCreate( ip,res) {
 // call geolocation api and get the details
    getGeolocation( ip ).then( response => {
 // update the database - map in this case
        updateTable(response);
 // set the header and status code success and return the details of the ip
        res.setHeader('content-type', 'Application/json');
        res.statusCode = 200;
        res.end(JSON.stringify(ip_table.get(ip)));
 
    },
    error => {
        res.statusCode = 400;
        res.end(error);
    } )
 
}//End of handle create

با اجرا شدن این ایندپوینت، سرویس ما شروع به مکان‌یابی آدرس‌های IP جدید می‌کند. از آنجایی که ما از یک سرویس ثالث (IP Geolocation API) استفاده می‌کنیم، این هندلر در واقع آن سرویس را اجرا می‌کند.

در گام بعدی نوبت به هندلر ( )handleShow می‌رسد.

function handleShow(res){
 // set the header and status
    res.setHeader('content-type', 'Application/json');
    res.statusCode = 200;
 // create an array from the map 
    array = Array.from(ip_table, ([name, value]) => ({ name, value }));
 // send the response array
    res.end(JSON.stringify(array));
}

این کد تمام IPهای در حال رهگیری را نمایش می‌دهد. حالا نوبت هندلر ( )handleRead است.

// function to handle read request
function handleRead(ip,res){
 // check if the ip is present in table
 if(ip_table.has(ip)){
 // set header, status code and send the entry
        res.setHeader('content-type', 'Application/json');
        res.statusCode = 200;
        res.end(JSON.stringify(ip_table.get(ip)));
    } else {
 // ip not found send error
        res.statusCode = 404;
        res.end(&quotRead: IP &quot+ip+&quot not found&quot);
    }
 
}

این کد اطلاعات IP خواسته شده را از آبجکت Map باز می‌گرداند. در قدم بعد باید هندلر ( )handleDelete را بنویسیم.

// function to handle delete request
function handleDelete(ip,res){
 // check if ip is in the table
 if(ip_table.has(ip)){
 // delete and send the success response
        ip_table.delete(ip);
        res.statusCode = 200;
        res.end(&quotDelete:  &quot+ip+&quot deleted&quot);
    } else {
 // ip not found send error
        res.statusCode = 404;
        res.end(&quotDelete: IP  &quot+ip+&quot not found&quot);
    }
 
}

این کد نیز IP خواسته شده را از Map پاک می‌کند. در آخر، نوبت نوشتن هندلر ( )handleUpdate است.

// function to handle update request
function handleUpdate(ip,res){
 // check if the ip is present in table
 if(ip_table.has(ip)){
 // update the details by calling geoplocation api
        getGeolocation( ip ).then( response => {
 // set header, status code, update table and send response
            res.setHeader('content-type', 'Application/json');
            res.statusCode = 200;
            updateTable(response);
            res.end(JSON.stringify(ip_table.get(ip)));
 
        },
        error => {
 // send error 
            res.statusCode = 400;
            res.end(error);
        } );
    } else {
 // ip not found in table so send error 
        res.statusCode = 404;
        res.end(&quotUpdate: IP &quot+ip+&quot not tracked&quot)
    }   
}

هر بار که اپلیکیشن مشتری قصد بررسی وضعیت جغرافیایی فعلی یک IP موجود را داشته باشد، درخواست به‌روزرسانی می‌شود.

مرحله چهارم: اضافه کردن فانکشن‌های داخلی

دو فانکشن ( )getGeolocation و ( )updateTable توسط هندلرها استفاده می‌شوند که باید آن‌ها را اضافه کنیم.

const rapidAPIBaseUrl = &quothttps://rapidapi.p.rapidapi.com/json/?ip=&quot
function getGeolocation( ipAddress ) {
 // initilize http.rquest object
            var req = https.request;
 // initialize header with the required information to call geolocation api. 
            var header = {
 &quotx-rapidapi-host&quot: &quotip-geolocation-ipwhois-io.p.rapidapi.com&quot,
 &quotx-rapidapi-key&quot: &quot<YOUR_RAPIDAPI_KEY>&quot,
 &quotuseQueryString&quot: true
                        };
 // add the query string including the IP address
            var query_string = { &quotip&quot : ipAddress };
 // set the options parameter
            var options = {
                                headers: header,
                                query: query_string
                            };
 // form the url
 const url =  rapidAPIBaseUrl + ipAddress ;
 
 return new Promise ( ( resolve, reject) => {
 
                    https.get( url, options, res  => {
 
                        let data = &quot&quot
 //data is received in chunks, so uppend data as and when received
                        res.on( 'data', function(response) {
                                    data = data + response;
                        });
 // handle error if any
                        res.on( 'error', function(err) {
                            console.log(&quotError&quot);
                            console.log(err);
                        })
 // if end of data return the final chunk
                        res.on( 'end', () => {
                            resolve(  JSON.parse(data) ); 
                        });
                    });//Endn of http
            }); //end of return promise
}//End of getGeolocation

این فانکشن تعامل و تبادل داده با IP Geolocation API را مدیریت می‌کند. در نظر داشته باشید به جای YOUR_RAPIDAPI_KEY باید کلیدی که در هنگام ثبت‌نام در RapidAPI را دریافت کرده‌اید، جایگزین کنید.

در ادامه کد زیر را در server.js وارد کنید:
function updateTable(entry){
 // get current date to update last update the time
    var time = new Date();
 // add the entry to table ip is the key and country, city and last updated time are stored
    ip_table.set(entry.ip, {&quotip&quot:entry.ip,&quotcountry&quot:entry.country,&quotcity&quot:entry.city,&quotlastUpdated&quot:time});
 
}

این فانشکن اطلاعات را در آبجکت Map وارد می‌کند.

جدا از موارد گفته شده، فانکشن داخلی ( )extractIPAddress آخرین فانکشنی است که باید اضافه شود. این فانکشن IPها را اندپوینت‌های API را استخراج و تایید می‌کند.

function extractIPAddress(path){
 
  var ipAddress = path.substring(path.lastIndexOf(&quot/&quot)+1,path.length);
 if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipAddress))
    {
 return ipAddress;
    }
 else
    {
      console.log(&quotinvalid IP Address&quot)
 return &quot&quot
    }
} // End of extractIPAddress

با نوشتن این کد، شما رابط کاربری یک API را به طور کامل نوشته‌اید. یادتان نرود فایل را ذخیره کنید.

مرحه پنجم: اجرای سرور

با استفاده از دستور زیر فایل server.js را اجرا کنید:

node server.js

اگر خطایی وجود نداشته باشد، این برنامه سرویس IPMon را به عنوان یک API در آدرس http://localhost:4000 اجرا می‌کند.

API خودتان را تست کنید

حالا نوبت به تست API می‌رسد. یک ترمینال جدید باز و کد زیر را اجرا کنید:

curl -X POST http://localhost:4000/api/ipmon/ip?ip=64.23.14.10
C:\>curl -X POST http://localhost:4000/api/ipmon/ip?ip=64.23.14.10
{&quotip&quot:&quot64.23.14.10″,&quotcountry&quot:&quotUnited States&quot,&quotcity&quot:&quotChicago&quot,&quotlastUpdated&quot:&quot2020-11-03T18:08:44.265Z&quot}

این کد 64.23.14.10 و اطلاعات آن را به پایگاه داده IPها برای رهگیری اضافه می‌کند.

حالا باید اندپوینت را برای بررسی وجود IP اجرا کنید.

curl -X GET http://localhost:4000/api/ipmon/ip/show
C:\>curl -X GET http://localhost:4000/api/ipmon/ip/show
[{&quotname&quot:&quot64.23.14.10″,&quotvalue&quot:{&quotip&quot:&quot64.23.14.10″,&quotcountry&quot:&quotUnited States&quot,&quotcity&quot:&quotChicago&quot,&quotlastUpdated&quot:&quot2020-11-03T18:08:44.265Z&quot}}]

شما همین‌طور می‌توانید اطلاعات یک IP خاص را دریافت کنید:

curl -X GET http://localhost:4000/api/ipmon/ip/64.23.14.10
C:\>curl -X GET http://localhost:4000/api/ipmon/ip/64.23.14.10
{&quotip&quot:&quot64.23.14.10″,&quotcountry&quot:&quotUnited States&quot,&quotcity&quot:&quotChicago&quot,&quotlastUpdated&quot:&quot2020-11-03T18:08:44.265Z&quot}

همچنین شما می‌توانید یک آدرس IP را به‌روز کنید:

curl -X PUT http://localhost:4000/api/ipmon/ip/64.23.14.10
C:\>curl -X PUT http://localhost:4000/api/ipmon/ip/64.23.14.10
{&quotip&quot:&quot64.23.14.10″,&quotcountry&quot:&quotUnited States&quot,&quotcity&quot:&quotChicago&quot,&quotlastUpdated&quot:&quot2020-11-03T18:16:10.168Z&quot}

و در آخر، نوبت به حذف یک IP می‌رسد:

curl -X DELETE http://localhost:4000/api/ipmon/ip/64.23.14.10
C:\>curl -X DELETE http://localhost:4000/api/ipmon/ip/64.23.14.10
Delete:  64.23.14.10 deleted

حالا چون IP حذف شده است، هر درخواست دیگری برای فراخوانی اطلاعات آن خطا برمی‌گرداند:

C:\>curl -X GET http://localhost:4000/api/ipmon/ip/64.23.14.10
Read: IP 64.23.14.10 not found

اگر همه چیز بدون مشکل اجرا شده است، تبریک می‌گوییم، شما با موفقیت یک API ساده نوشته‌اید.