แนวทางสร้าง RESTful API ด้วย Express เบื้องต้น

เขียนเมื่อ 4 ปีก่อน โดย Ninenik Narkdee
nodejs expressjs restful api

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ nodejs expressjs restful api

ดูแล้ว 13,687 ครั้ง


เนื้อหาต่อไปนี้ เราจะมาดูเกี่ยวกับ การสร้าง RESTful API โดยใช้ Express
    RESTful API เป็นรูปแบบหนึ่งของบริการ การส่งผ่านข้อมูลระหว่าง server 
หรือ web application นิยมใช้เป็น web service เช่น
ให้บริการข้อมูลบางรายการที่มีการร้องขอด้วย HTTP GET Request
สร้างรายการข้อมูลใหม่ด้วย HTTP POST Request
อัพเดทรายการข้อมูลที่ร้องขอโดยส่งค่า parameter เข้ามาอัพเดท ด้วย HTTP PUT Request
หรือลบรายการข้อมูลด้วย  HTTP DELETE Request
    ในที่นี้เรายังไม่ได้ลงไปในรายละเอียด ของการใช้งานร่วมกับฐานข้อมูล แต่เราจะใช้ชุดข้อมูล
จำลองสำหรับทดสอบประกอบ
 
 

แบบจำลองข้อมูล USERS

    เริ่มต้นให้เราสร้างไฟล์ mock-users.js เป็นไฟล์ข้อมูลจำลอง รายการ users ที่เป็น Array ของ object
ไว้ที่เดียวกับไฟล์ app.js จะได้เป็นดังนี้
 
    ไฟล์ mock-users.js
 
 

 
const users = [
    {
        "id":1,
        "name":"John Doe",
        "email":"john.doe@gmail.com"
    },
    {
        "id":2,
        "name":"William Smith",
        "email":"will.smith@gmail.com"
    },
    {
        "id":3,
        "name":"Jennifer Lee",
        "email":"jennifer.lee@gmail.com"
    }        
]
module.exports = users  // ส่งออกข้อมูลไปใช้ยัง module อื่นๆ
 
 

การสร้าง RESTful API ด้วย Express

    ในที่นี้ เราจะสร้าง api สำหรับใช้งานข้อมูล users โดยใช้ให้เรียกไปยัง path: "/api/users" และ
path: "/api/user/:id"
    โดยใน path: "/api/users" เราจะใช้กับ 
        GET request ที่ดึงข้อมูล users ทั้งหมด สังเกตว่าเราใช้ "s" ต่อท้าย
        POST request ที่ใช้สำหรับเพิ่มข้อมูล user ไปยังรายการ users ทั้งหมด
    และใน path: "/api/user/:id" เราจะใช้กับ
        GET request ดึงข้อมูลเฉพาะ user id ที่ส่งเข้ามา
        PUT request สำหรับอัพเดทข้อมูลเฉพาะ user id ที่ต้องการอัพเดท
        DELETE request สำหรับลบข้อมูลเฉพาะ user id ที่ต้องการลบ
 
    หมายเหตุ: การกำหนด route path เราอาจจะกำหนดเป็นอย่างใดอย่างหนึ่งเลยก็ได้ โดยไม่ต้องสนใจว่าจะ
มีหรือไม่มี "s" เช่น อาจจะใช้เป็น "/api/user" และ "/api/user/:id"  แต่ในที่นี้ ต้องการแยกความเป็นพหุพจน์
หรือของหลายๆ สิ่ง กับเอกพจน์หรือของสิ่งเดียว จึงใช้รูปแบบที่มีและไม่มี "s"
 

 

รูปแบบ Routes สำหรับ RESTful API

    รูปแบบโค้ดด้านล่าง เป็นการใช้งาน การหนด routes สำหรับสร้าง RESTful API ให้กับข้อมูล users สังเกตว่า
ในส่วนของฟังก์ชั่น หรือ middleware ฟังก์ชั่นเราใช้งานแบบ Arrow ฟังก์ชั่น เพื่อให้โค้ดกระชับขึ้น
 
// แสดงรายการทั้งหมด
app.get('/api/users', (req, res) => {
    res.send('GET Request all')
})

// เพิ่มรายการใหม่ เข้าไปในรายการทั้งหมด
app.post('/api/users', (req, res) => {
    res.send('POST Request')
})

// แสดงรายการตามค่า id ที่ต้องการ
app.get('/api/user/:id', (req, res) => {
    res.send('GET Request for any ID')
})

// อัพเดทรายการตามค่า id ที่ต้องการ
app.put('/api/user/:id', (req, res) => {
    res.send('PUT Request for any ID')
})

// ลบรายการตามค่า id ที่ต้องการ
app.delete('/api/user/:id', (req, res) => {
    res.send('DELETE Request for any ID')
})
    ซึ่งเราสามารถใช้งานคำสั่ง app.route() หรือ router.route() เพื่อกำหนด route path ที่เหมือนกันแล้วรวม
Method ต่างๆ ไว้แบบ chain หรือเชื่อมต่อเนื่องกันในลักษณะแบบนี้ก็ได้ 
 
app.route('/api/users')
    .get((req, res) => {
        res.send('GET Request all')
    })
    .post((req, res) => {
        res.send('POST Request')
    })

app.route('/api/user/:id')
    .get((req, res) => {
        res.send('GET Request for any ID')
    })
    .put((req, res) => {
        res.send('PUT Request for any ID')
    })
    .delete((req, res) => {
        res.send('DELETE Request for any ID')
    })
    ในการกำหนด route path หากเราต้องการระบุว่า user(s) จะมี "s" ต่อท้ายหรือไม่มีก็ได้ ก็สามารถกำหนดเป็น
app.route('/api/users?') และ app.route('/api/users?/:id') ดูเพิ่มเติมในเนื้อหาการใช้งาน route http://niik.in/908
ตัวอย่างข้างต้น เราใช้ res.send() สำหรับส่งข้อมูลกลับไปแสดง เพื่อทดสอบการทำงานเท่านั้น ในความเป็นจริง
การสร้าง RESTful API จะส่งข้อมูลกลับออกมาในรูปแบบ JSON String data  รวมถึงการส่ง status กลับมาด้วย
โดยจะใช้คำส่ัง 
    res.status([status code]).json([json string object])
    // ตัวอย่าง res.status(400).json({"status":400,"message":"Not found user with the given ID"})
    หรือ 
    res.json([json string object])
    // ตัวอย่าง 
    // const result = {
    //     "status":200,
    //     "data":user
    // }
    // res.json(result)	  
    การกำหนดรูปแบบของข้อมูลที่จะส่งกลับมายังผู้ใช้งาน ขึ้นอยู่กับรุปแบบ API ตามที่เรากำหนด ว่าต้องการใช้ค่าอะไร
หรือต้องการรูปแบบไหน สำหรับกรณีของเราในที่นี้ จะส่งกลับข้อมูลในรูปแบบ   ดังนี้
    กรณีเกิด error
    ส่งกลับค่า status และ message property
 
{
	"status":400, 
	"message":"Not found user with the given ID"
}
    กรณีสำเร็จไม่เกิด error
    ส่งกลับค่า status และ data  
 
{
    "status": 200,
    "data": {
        "id": 1,
        "name": "John Doe",
        "email": "john.doe@gmail.com"
    }
}
 
 

การสร้าง RESTful API ด้วย router

    ในที่นี้ เราจะใช้ router สร้าง RESTful API สำหรับข้อมูล users แทน การกำหนดผ่าน app instance เพื่อ
ให้สะดวก และรองรับการสร้าง API ให้กับข้อมูลอื่นๆ เพิ่มเติม ให้เราสร้างไฟล์ users.js ไว้ในโฟลเดอร์ routes
 
    ไฟล์ users.js 
 
const express = require('express')
const router = express.Router()
const users = require('../mock-users') // ใช้งานข้อมูลจำลองจากไฟล์ mock-users.js

// กำหนด route แบบ chain 
router.route('/users?')
    .get((req, res, next) => { // GET มาที่ path: "/users" หรือ "/user"
        // ส่งข้อมูลทั้งหมด กลับออกไปแสดง
        const result = { // รูปแบบผลลัพธ์ข้อมูลที่จะส่งกลับ
            "status":200,
            "data":users
        }
        return res.json(result)  
    })
    .post((req, res, next) => {// POST มาที่ path: "/users" หรือ "/user"
        let user = {
            "id":users.length+1, // เพิ่ม id จากค่าเดิมไปอีก 1
            "name":req.body.name, // ใช้ค่า name จากที่ส่งเข้ามา
            "email":req.body.email // ใช้ค่า email จากที่ส่งเข้ามา
        }
        users.push(user) // เพิ่มข้อมูลใหม่เข้าไปใน array users
       const result = { // รูปแบบผลลัพธ์ข้อมูลที่จะส่งกลับ
            "status":200,
            "data":users
        }
        return res.json(result)  
    })    

// กำหนด route แบบ chain และมี id params เพิ่มเข้ามา    
router.route('/user/:id')
    .all((req, res, next) => {// ทุกๆ Request มาที่ "/user/:id"
        // หาว่า ใน users มี user.id ที่ตรงกับ params.id หรือไม่ ถ้ามี ก็เก็บ user นั้นๆ
        let user = users.find( (user) => user.id === parseInt(req.params.id))
        // ถ้าไม่มี ก็ส่งสถานะ 400 และข้อความกลับออกไป
        if(!user) return res.status(400).json({"status":400,"message":"Not found user with the given ID"})
        res.user = user // กรณีข้อมูล user ส่งต่อไปยังฟังก์ชั่นถัดไป
        next()
    })
    .get((req, res, next) => {// GET มาที่ path: "/user/:id" ใดๆ
        // ส่งข้อมูล user ที่หาเจอ และได้มาจากฟังก์ชั่นก่อนหน้า ส่งออกไป
        const result = {
            "status":200,
            "data":res.user
        }
        return res.json(result)
    })
    .put((req, res, next) => {// PUT มาที่ path: "/user/:id" ใดๆ
        let user = { // กำหนดข้อมูลที่จะอัพเดท
            "id":res.user.id, // อัพเดท id mี่ตรงกับค่า params.id
            "name":req.body.name, // ใช้ข้อมูลใหม่ที่ส่งเข้ามาสำหรับ name
            "email":req.body.email // ใช้ข้อมูลใหม่ที่ส่งเข้ามาสำหรับ email
        }
        // ส่งข้อมูลที่ได้อัพเดทเรียบร้อยแล้ว กลับออกไป
        const result = {
            "status":200,
            "data":user
        }
        return res.json(result)        
    })
    .delete((req, res, next) => {// DELETE มาที่ path: "/user/:id" ใดๆ
        // จำลองการลบข้อมูล ในที่นี้ ให้แสดงเฉพาะข้อมูลที่เหลืออยู่ หรือก็คือ ข้อมูลที่ไม่่ตรงกับ params.id ที่ส่งมา
        let user = users.filter( (user) => user.id !== parseInt(req.params.id))
        const result = {
            "status":200,
            "data":user
        }
        return res.json(result)
    })            

module.exports = router
    จะเห็นว่ารูปแบบ API ข้างต้น เรายังไม่ได้มีของการตรวจสอบข้อมูลทีส่งเข้ามาเพื่อเพิ่ม หรือเพื่ออัพเดท
ซึ่งตรงส่วนนี้เราจะได้ศึกษาภายหลังในลำดับ ต่อๆ ไป
    จากโค้ดเรากำหนดการทำงานของ router.route() ไว้สองส่วน คือส่วนที่มีการส่ง และไม่มีการส่งค่า params.id
เข้ามาด้วย ซึ่งส่วนที่มีการส่งค่า params.id เข้ามา เราจะมีการตรวจสอบว่า ค่านั้น ตรงกับข้อมูลจำลองของเราหรือไม่
หรือก็คือ ไม่ข้อมูลนั้นๆ ตรงตามค่า id ที่ส่งมาหรือไม่ ถ้าไม่มี ก็ให้แจ้ง error โดยกำหนดสถานะ และข้อความกลับออกมา
    หากสังเกต จะพบว่าในการส่งข้อมูลในรูปแบบ JSON string data เรามีการใช้คำสั่ง return เข้าไปด้วย ทั้งนี้ก็
หมายความว่า หลังจากส่งข้อมูลออกไปยังผู้ใช้แล้ว ก็ให้ออกจากการทำงานของ router ไปเลย ไม่ต้องสนคำสั่ง
ที่อยู่ด้านล่าง เพราะเราไม่ได้ใช้งานแล้ว  คำถามคือ หากเราไม่กำหนด return โค้ดยังทำงานปกติหรือไม่ คำตอบก็
คือ โค้ดในฝั่ง server ยังทำงานปกติต่อ ถ้ายังมีคำสังอื่นๆ แต่ request / response cycle ได้จบลงแล้วเมื่อมีการส่ง
ข้อมูลกลับไปแสดงที่ฝั่งผู้ใช้  คำสั่ง return จึงเหมาะ กรณีเรามีคำสั่งอื่นๆ หรือโค้ดจำนวนมากๆ ที่กำหนดการทำงาน
ไว้กับ route path เดียวกัน แต่เราต้องการข้ามส่วน นั้นๆ ก็สามารถใช้คำสั่ง return เพิ่มเข้าไปได้
 
    เมื่อสร้าง router module กำหนด API ให้กับข้อมูล users เรียบร้อยแล้ว เราก็เรียกใช้งานในไฟล์ app.js ดังนี้
 
    ไฟล์ app.js
 
 

 
const express = require('express')  // ใช้งาน module express
const app = express()  // สร้างตัวแปร app เป็น instance ของ express
const path = require('path') // เรียกใช้งาน path module
const createError = require('http-errors') // เรียกใช้งาน http-errors module
const port = 3000  // port 
 
// ส่วนของการใช้งาน router module ต่างๆ 
const indexRouter = require('./routes/index')
const userRouter = require('./routes/users')
 
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.set('view options', {delimiter: '?'});
// app.set('env','production')
 
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(express.static(path.join(__dirname, 'public')))
 
// เรียกใช้งาน indexRouter
app.use('/', indexRouter)
app.use('/api', userRouter) 

// ทำงานทุก request ที่เข้ามา 
app.use(function(req, res, next) {
    var err = createError(404)
    next(err)
})
 
// ส่วนจัดการ error
app.use(function (err, req, res, next) {
    // กำหนด response local variables 
    res.locals.message = err.message
    res.locals.error = req.app.get('env') === 'development' ? err : {}
 
    // กำหนด status และ render หน้า error page
    res.status(err.status || 500) // ถ้ามี status หรือถ้าไม่มีใช้เป็น 500
    res.render('error') 
})
 
app.listen(port, function() {
    console.log(`Example app listening on port ${port}!`)
})
 
 

ทดสอบการทำงาน RESTful API

    ต่อไปเราจะมาทดสอบการทำงาน RESTful API ที่เราสร้างด้วย Express ในที่นี้ เราจะใช้ Postman ซึ่งเป็นเครื่องมือ
สำหรับใช้งาน API สามารถไปดาวน์โหลดมาใช้งาานได้ที่ POSTMAN
 

    GET Request แสดงทุกรายการ

    เราจะมาเริ่มที่ GET Request ไปที่ "/api/users" ซึ่งเดิมเรามีข้อมูลจำลอง 3 รายการ

 


 

    POST Request เพิ่มข้อมูล

    ต่อด้วย POST Request ไปที่ "/api/users" โดยมีข้อมูลส่งไปด้วย 
    กำหนด header "Content-Type":"application/json"
 
 

 
 
    แล้วก็กำหนดข้อมูลในรุปแบบ JSON object เพื่อส่งข้อมูลไปเพิ่ม
 
 

 
 
    ผลลัพธ์เมื่อทำการเพิ่มข้อมูลเข้าไปด้วย POST request จะมีข้อมูลเพิ่มมาอีก 1 เป็นทั้ง 4 รายการ

 

 
 

    GET Request แสดงเฉพาะรายการ

    ทำการ GET Request ไปยัง id รายการที่ต้องการแสดงข้อมูล เช่น ไปที่ path: "/api/user/1"
 
 


 
 

    PUT Request อัพเดทเฉพาะรายการ

    ทำการ PUT Request เพื่อส่งข้อมูลไปอัพเดท ยังรายการที่มี id ที่กำหนด โดยส่งข้อมูลไปด้วย 
 
 

 
 
    ผลลัพธ์ที่ได้ เมื่อทำการส่งข้อมูลไปอัพเดท
 
 


 
 

    DELETE Request ลบเฉพราะรายการ

    ทำการ DELETE Request เพื่อส่ง id รายการที่ต้องการลบ เข้าไป
 
 

 
 
    ผลลัพธ์ที่ได้ เมื่อลบรายการ id เท่ากับ 1 ออกไป จะเหลือ 3 รายการ

 
 

    BAD Request

    เราลองทำการ GET Reqeust ไปยัง id ที่ไม่มีอยู่ เช่น 10 จะได้ผลลัพธ์กรณี error ดังนี้
 
 

 
 
    กรณีเรากำหนด params id ที่ไม่มีข้อมูลอยู่ ไม่ว่าจะใช้ GET PUT หรือ DELETE ก็จะขึ้น error ในลักษณะ
เดียวกันกับตัวอย่างด้านบน
 
    การสร้าง RESTful API ด้วย Express สามารถทำได้ง่ายและสะดวก สามารถต่อยอดสร้าง API สำหรับ
พัฒนา Mobile App หรือ Web App ร่วมกับ Framework ต่างๆ ได้ 
    ในเนื้อหาตอนหน้า เราจะมาดูในส่วนของการกำหนดรูปแบบการตรวจสอบข้อมูลที่ส่งเข้ามา เช่น ในกรณี
การ POST Request / PUT Request 


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ