RESTFul API with Node.js + Express


วันนี้จะมาเขียนเกี่ยวกับการทำ RESTFul API บน Node.js Platform นะ
โจทย์ของเราไม่มีอะไรยากเลย เป็นแค่ Simple API ที่จะจัดการข้อมูล User ในระบบ ซึ่งจะมี Operation ทั้งหมด 4 Operation คือ
  • ดึงข้อมูล User ทั้งหมด
  • เพิ่ม User โดยส่งชื่อและอายุ
  • แก้ไขข้อมูล User โดยอ้างอิงจาก id และส่งข้อมูลที่ต้องการแก้ไข (ชื่อ และ/หรือ อายุ)
  • ลบข้อมูล User โดยอ้างอิงจาก id
เริ่มต้นที่ผมสร้าง Folder ใหม่ชื่อ nodeapi จากนั้นก็สร้าง package.json เพื่อใส่ dependency ที่ใช้
เรียบร้อยแล้วก็ลง Dependency กันก่อน โดยเข้าไปที่ Folder เราแล้วพิมพ์
จากนั้นการที่จะทำ RESTFul API ผ่านตัว Node.js เลย อาจจะเขียนไม่ง่ายเท่าไร วันนี้ก็เลยใช้ตัวช่วยที่ชื่อว่า Express ซึ่งเป็น Web Framework ที่เป้นนิยมบน Node.js Platform แต่เราจะเอา Library ตัวนี้มารับ HTTP Request เท่านั้น ต่อไปสร้าง index.js มาเริ่มเขียนโปรแกรมกันดีกว่า
var express = require('express');
var app = express();
var http = require('http');

var userService = require('./user');

// Setup Express MiddleWare
app.set('port', process.env.PORT || 3000);
app.use(express.json());
app.use(express.bodyParser());
app.use(express.urlencoded());

if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

// User API Interface
app.get('/api/users', userService.list);
app.post('/api/user', userService.create);
app.put('/api/user', userService.update);
app.del('/api/user', userService.erase);

// Start Server and Waiting for Request
http.createServer(app).listen(app.get('port'), function(){
 console.log('\nNode RESTFul API Example by iMacbaszii');
  console.log('Server listening on port ' + app.get('port'));
});

[Line 1] Code เริ่มจากการประกาศ Module ที่ต้องเรียกใช้ ซึ่งผมแยก API Interface และ Implementation ออกจากกัน เพื่อความสวยงาม ซึ่งจะเขียนไว้ที่ไฟล์ user.js ที่จะให้สร้างต่อไป 

[Line 7-15] จากนั้นก็เขียนการเรียกใช้ Middleware ของ Express ซึ่งเท่าที่ต้องใช้สำหรับการทำ RESTFul API ก็มีเท่านี้แหละครับ

[Line 18-21] API Interface

[Line 24-27] เป็นคำสั่งในการเปิด Node.js Server โดยจะใช้ HTTP Module ของ Node.js ในการเปิด Server ที่ port 3000 ที่ได้กำหนดไว้ด้านบน

เรียบร้อยแล้ว สร้าง user.js ตามที่บอกไว้เลย เพื่อ Implement API ที่ประกาศเป็น Interface ไว้นะ
เริ่มจากการประกาศของที่ต้องใช้ทั้งหมด ผมจะบอกคร่าวๆ แล้วกันนะ
  • moment คือ Library จัดการเกี่ยวกับเรื่องเวลา (momentjs.com)
  • underscore คือ Library ที่รวบรวม Helper Function ต่างๆ (underscorejs.org)
  • chance คือ Library ที่จะใช้สุ่ม (chancejs.com)
จากนั้นก็ประกาศ Users เป็น Array ที่เก็บข้อมูล User ทุกคน (ใน Blog นี้ไม่ได้สอนการ Permanent ข้อมูลไว้นะ เวลา Restart Server ข้อมูลทั้งหมดก็หายไป ... เพราะเราจะ Focus ที่การเขียน API) 
โดยข้อมูลของ User ผมประกาศเป็น Class Function ไว้โดยมี Property ตามนั้นแหละ :P
var moment = require('moment');
var _ = require('underscore');
var Chance = require('chance'),
  chance = new Chance();
  
var users = [];

function User(id, name, age, createdAt, updatedAt) {
 this.id = id;
 this.name = name;
 this.age = age;
 this.createdAt = createdAt;
 this.updatedAt = updatedAt;
}
อย่างที่ได้บอกในตอนแรก RESTFul API คือการเขียน API ที่ใช้ Protocol ของ HTTP Request เพื่อทำอะไรบางอย่างกับ Data ที่อยู่บน Server แบ่งเป็น 4 Methods นั่นคือ
  • GET ใช้ในการดึงข้อมูลของ Data นั้นๆ ทั้งหมดบน Server
  • POST ใช้ในกรณีที่มีการสร้าง Data ขึ้นบน Server
  • PUT ใช้การแก้ไข/อัพเดทข้อมูลบน Server
  • DELETE ใช้ในการลบข้อมูลนั้นจาก Server
โดยที่ GET จะรู้กันว่า ดึงข้อมูลทั้งหมด ผมเลยกำหนด URL ในการ Access API เป็นชื่อ Object ที่เป็นพหูพจน์ แต่ POST, PUT, DELETE มักจะกระทำกับ Object เพียงหนึ่งตัว จึงใช้เป็นเอกพจน์

list = function(req, res) {
 res.json(users);
}

create = function(req, res) {
 var user, now, id;
 id = chance.hash({ length: 6 });
 now = moment().format('DD MMMM YYYY, HH:mm:ss');
 
 user = new User(id, req.body.name, req.body.age, now, now);
 users.push(user);
 
 res.json(user);
}

update = function(req, res) {
 var user, id, newName, newAge, updateTime, userToUpdate;
 
 id = req.body.id;
 userToUpdate = _.find(users, function(user) { 
  return user.id === id;
 });
  
 newName = (req.body.newName != undefined) ? req.body.newName : userToUpdate.name;
 newAge = (req.body.newAge != undefined) ? req.body.newAge : userToUpdate.age;
 updateTime = moment().format('DD MMMM YYYY, HH:mm:ss');
 
 updateIndex = users.indexOf(userToUpdate);
 
 if (updateIndex == -1) {
  res.json(400, { 'error': { message: 'User Not Found' }});
 } else {
  users[updateIndex] = new User(userToUpdate.id, newName, newAge, userToUpdate.createdAt, updateTime);
  res.json({ status: 'success', message: 'UserID ' + userToUpdate.id + ' has been updated'});
 }
}

erase = function(req, res) {
 var id, oldSize;
 id = req.body.id;
  
 oldSize = users.length;
 users = _.filter(users, function(user) {
  return user.id !== id;
 });
  
 if (users.length == oldSize) {
  res.json(400, { 'error': { message: 'User Not Found' }});
 } else {
  res.json({ status: 'success', message: 'UserID ' + id + ' has been deleted'});
 }
}


และไฟล์จะไม่สามารถเรียก Function ที่เราประกาศขึ้นในนี้ ถ้าไม่มีการ Export ออกไป เราจึงใส่ Code นี้ปิดท้าย เพื่อให้ไฟล์อื่น สามารถเรียกใช้ Function ที่อยู่ใน Module นี้ได้

module.exports.list = list;
module.exports.create = create;
module.exports.update = update;
module.exports.erase = erase;

ไม่ยากเลยใช่ไหมครับ :) ถึงจะไม่ง่ายขนาด Full Stack Web Framework ที่ Generate ของง่ายๆ พวกนี้ได้อย่าง Rails แต่ก็คนละอารมณ์กัน ในการเขียน API นะครับ ลองนำไปศึกษาดู

ใครอ่านมาถึงตรงนี้ได้ ถ้าเขียนกันไม่ถูก ผมจัดโค้ดของ Example ไว้ที่ Github นะครับ
https://github.com/macbaszii/SimpleNodeAPI

Popular posts from this blog

12 วิธี การบริการและดูแลลูกค้าในร้าน Starbucks

[Android Dev] การติดตั้ง Eclipse+AndroidSDK เพื่อพัฒนาโปรแกรมบน Android

"อีสุกอีใส" ประสบการณ์เมื่อต้องมาเป็นตอนอายุ 22