CRUD Rest API โดยใช้ Express ของ Node.js

บทความนี้ เราจะกลับมาพูดถึงโลกของ Node อีกครั้ง หลังจากที่ไปงมอยู่กับ Flutter มาพักใหญ่ๆ (ซึ่งสนุกมาก และกำลังจะมี Flutter 2 ออกมาด้วย เห็นว่าคราวนี้รองรับ web แบบเต็มสตรีม) ในบทความนี้จะเป็นการจดบันทึกการสร้าง Rest API ใช้สำหรับ สร้าง, ดึง, แก้ไข และลบข้อมูล (Create, Read, Update, Delete : CRUD)
โดยเราได้มีโอกาสนั่งดูคลิปสอนการสร้าง CRUD และใช้กับ React ของ คุณปฏิภาณ เค้าสอนค่อนข้างดีเลย เคลียร์มาก ในคลิปมีสอนวิธีสร้าง Backend Application ขึ้นมาเป็น API เพื่อรอ Request การรับ-ส่งข้อมูลจาก Frontend คือ React นั่นเอง ดังนั้น ซึ่งการสร้าง API ขอสรุปเป็นขั้นตอนดังต่อไปนี้ ไปดูกัน

ติดตั้ง Node และ Libraries

เริ่มจากเราสร้าง folder ขึ้นมาก่อน เปิด Terminal ขึ้นมา direct ไปยัง folder แล้วสร้าง json file และ library ของ Node ขึ้นมาโดยใช้คำสั่ง

npm init

หรือถ้าอยากสร้างขึ้นมาแบบ set dafault เลยก็ใช้

npm init -y

รอจนโหลดเสร็จแล้ว ให้ติดตั้ง library 4 อันคือ body-parser, cors, express, mongoose

npm i body-parser cors express mongoose

จากนั้น ทำการติดตั้ง nodemon เพื่อให้รันไฟล์ได้แบบต่อเนื่องไม่หลุด ถึงแม้ระหว่างทางที่เราโค้ด จะเจอ error ใดๆ

npm i nodemon --save-dev

ที่ใส่ --save-dev เพิ่มเข้ามาเพื่อต้องการให้ติดตั้ง nodemon ให้อยู่ในโหมด development

เมื่อเสร็จแล้ว เข้าไปเช็คการติดตั้งได้ที่ package.json

เข้าไปในส่วน scripts ให้เพิ่ม server key แบบนี้

"server": "nodemon index"

เพื่อใช้เป็นคำสั่งสำหรับสั่งรันด้วย nodemon start file index ขึ้นมานั่นเอง เวลาสั่งรันเราจะใช้คำสั่งนี้

npm run server

Node จะไปเรียกใช้ scripts ตรงส่วน sever ที่เราได้เพิ่มเข้าไปให้มาทำงาน และจะสตาร์ทตามที่ได้อธิบายไปข้างบน

เขียน API

เมื่อทำการติดตั้งเสร็จแล้ว ต่อมาเราจะทำการเขียน API เพื่อให้ client ยิงคำขอ (request) มายัง server เพื่อส่งหรือรับข้อมูล รวมทั้งยังสามารถแก้ไขข้อมูลที่ส่งมาก่อนหน้า และลบข้อมูลเมื่อไม่ต้องการได้ ซึ่ง Node ก็อนุญาตให้เขียน API แบบ free style จะเขียนทุกๆ module ไว้หน้าเดียวกันหมดก็ได้ หรือเขียนแยกแล้วค่อยเอามาประกอบร่างกันก็ได้ ซึ่งตัวอย่างนี้จะขอใช้วิธีหลัง เพราะง่ายต่อการอ่านโค้ดในภายภาคหน้า ว่าแล้วก็มาเริ่มทำตามขั้นตอนดังต่อไปนี้

สร้าง database

ต่อมาจะเป็นการสร้าง database เพื่อใช้เป็นที่เก็บข้อมูลทั้งหมดที่จะรับเข้าหรือส่งออกไปยัง client เราจะสร้างโดย สร้าง file ชื่อ db.js ภายในโฟลเดอร์ database

//db.js
module.exports = {
  db: "mongodb://localhost:27017/reactdb"
};

ในที่นี้ เราตั้งชื่อ database ชื่อว่า reactdb ซึ่งเราสามารถตั้งเป็นชื่ออะไรก็ได้

สร้าง models

ทำการตั้งโฟลเดอร์ ชื่อว่า models และสร้างไฟล์ภายใน เพื่อเป็น schema หรือโครสร้างการจัดข้อมูลในฐานข้อมูล ในที่นี้เราจะ import mongoose เข้ามาใช้งาน

//Stormtrooper.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;

let StormtrooperSchema = new Schema(
  {
    name: {
      type: String,
    },
    email: {
      type: String,
    },
    rollno: {
      type: Number,
    },
  },
  { collection: "stormtrooper" }
);

module.exports = mongoose.model("stormtrooper", StormtrooperSchema);

จากตัวอย่าง เราสร้างตัวแปร StormtrooperSchema เอาไว้เก็บค่าโครงสร้างข้อมูลที่ออกแบบมา ได้แก่ name จะเก็บข้อมูลรูปแบบ String, email เก็บข้อมูลรูปแบบ String, และ rollno เก็บข้อมูลนรูปแบบ Number
ส่วนชื่อ collection เราจะใช้ชื่อว่า stormtrooper
จากนั้นให้ส่งออก (export) ฟังก์ชั่นmongoose.model() ซึ่งใส่ argument สองตัว คือ ชื่อ collection และตัวแปรที่เก็บค่า schema

สร้าง routes

เราจะทำการสร้าง route ขึ้นมาเพื่อเป็นช่องทางติดต่อกับ server ของ client โดยจะเริ่มจากการ import lib ที่จำเป็นเข้ามาก่อน และรวมถึง schema ที่เราสร้างไปก่อนหน้าด้วย

let mongoose = require("mongoose"),
  express = require("express"),
  router = express.Router();

// Stormtrooper Model
let StormtrooperSchema = require("../models/Stormtrooper");

สร้างฟังก์ชั่น post() โดยภายในมี function ที่สร้างมาจาก create() เพื่อเป็น middleware function ขึ้นมาจาก route (ในที่นี้เท่ากับ express.Router() มีศักดิ์เป็น mini object ของ app ที่นิยมใช้กันอีกที)

//Create Stormtrooper
router.route("/create-stormtrooper").post((req, res, next) => {
  StormtrooperSchema.create(req.body, (error, data) => {
    if (error) {
      return next(error);
    } else {
      console.log(data);
      res.json(data);
    }
  });
});

สร้าง api เพื่อส่งข้อมูลไปยังผู้ใช้งาน ผ่าน get() ภายในทำการสร้าง middleware function เพื่อส่งข้อมูลกลับไป โดยใช้ find()

//Read Stormtrooper
router.route("/").get((req, res) => {
  StormtrooperSchema.find((error, data) => {
    if (error) {
      return next(error);
    } else {
      res.json(data);
    }
  });
});

หรือจะหาค่าเป็นทีละ id ก็ทำได้ โดยใช้ findById()

//Get Single Stormtrooper
router.route("/edit-stormtrooper/:id").get((req, res) => {
  StormtrooperSchema.findById(req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.json(data);
    }
  });
});

เมื่อมี create กับ read api แล้ว ก็ต้องมี api เพื่อใช้ usdate ข้อมูลใน db ด้วย การอัพเดทนั้น เราจะอัพเดททีละอัน ซึ่งใช้ function findByIdAndUpdate() สร้าง function ใน put()

//Update Stormtrooper
router.route("/update-stormtrooper/:id").put((req, res, next) => {
  StormtrooperSchema.findByIdAndUpdate(
    req.params.id,
    {
      $set: req.body,
    },
    (error, data) => {
      if (error) {
        console.log(error);
        return next(error);
      } else {
        res.json(data);
        console.log("Stormtrooper successfully updated");
      }
    }
  );
});

สุดท้ายก็สร้าง api ของการลบข้อมูลใน db ด้วย delete() เราจะลบข้อมูลทีละอัน เพราะฉะนั้นจะใช้ findByIdAndRemove() เป็น function ข้างใน middleware และสร้างเงื่อนไขเพิ่ม ถ้าการส่ง request สำเร็จ ให้ส่ง status(200) กลับมาด้วย แล้วต่อด้วย msg

//Delete Stormtrooper
router.route("/delete-stormtrooper/:id").delete((req, res, next) => {
  StormtrooperSchema.findByIdAndRemove(req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({ msg: data });
    }
  });
});

ปิดท้ายที่ export

module.exports = router;

สร้าง Server

มาถึงขั้นตอนที่เราจะต้องใช้ module ต่างๆที่ได้สร้างขึ้นไปมาประกอบร่างกันเป็น server แล้ว ขึ้นตอนแรกให้ import ของที่ต้องใช้มาทั้งหมด

let express = require("express"),
  mongoose = require("mongoose"),
  cors = require("cors"),
  bodyParser = require("body-parser"),
  dbConfig = require("./database/db");
//Express Route
const stormtrooperRoute = require("./routes/stormtrooper.route");

ทำการ Config mongoose ตามตัวอย่างด้านล่าง จากนั้น ใช้ mongoose.connect() เพื่อติดต่อ server เข้ากับ db

//Connecting MongoDB Database
mongoose.Promise = global.Promise;
mongoose.connect(dbConfig.db, { 
    useNewUrlParser: true, 
    useUnifiedTopology: true 
}).then(
  () => {
    console.log("database successfully connected");
  },
  (error) => {
    console.log("Could not connect to database:" + error);
  }
);

มีการใช้ mongoose.Promise = global.Promise เพื่อให้สามารถเซต promise ของ mongoose library ขึ้นมาได้ ซึ่งใน Mongoose 4 จะใช้เป็น mpromise แต่ยกเลิกไปใน Mongoose 5 เลย Promise แทน

ต่อมา สร้าง config โดยใช้ app.use() ตามโค้ดด้านล่าง

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use("/stormtrooper", stormtrooperRoute);

และกำหนด port เพื่อใช้ติดต่อรับ-ส่งข้อมูล โดย client จะต้องยิง request เข้ามาทาง port นี้ จากนั้น สร้าง listen ขึ้นมาเพื่อรอการสั่งทำงาน

const port = process.env.PORT || 5000;
const server = app.listen(port, () => {
  console.log(`Server is running in port ${port}`);
});

ดักจับ error ที่จะเกิดขึ้นจาก server

app.use((req, res, next) => {
  next(createError(404));
});

ถ้า server ไม่ตอบสนองหรือไม่ทำงาน ให้ส่งข้อความกลับมาทาง console ด้วยนะ

app.use((err, req, res, next) => {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Send request and receive response with Postman

เมื่อสร้าง API เสร็จแล้ว ให้ทดลองยิง request ผ่าน entry point ที่เราสร้างเอาไว้ เช่น

POST

ส่ง request ไปที่ /stormtrooper/create-stormtrooper และส่ง key value เป็น body ไปด้วย ตามตัวอย่าง

ส่งคำสั่ง Post แล้วส่งค่าไปใน Body

จากนั้น เมื่อกดส่ง เราจะได้ข้อมูลที่ส่งไปใน body จะถูกบันทึกลงในฐานข้อมูล

เมื่อส่งไปเรียบร้อยแล้ว

GET

ส่ง request ไปที่ /stormtrooper/ จะได้ data ออกมาจากฐานข้อมูลทั้งหมด

เรียกดูข้อมูลทั้งหมด

หรือเราสามารถส่ง request ไปเพื่อขอ data ทีละ <id> ได้ จากการส่ง request ไปที่ /stormtrooper/edit-stormtrooper/<id>

หรือจะเรียกดูทีละรายการก็ได้นะ

UPDATE

ส่ง request ไปที่ /stormtrooper/update-stormtrooper/<id> จากนั้น ใน body ใส่ค่า key value เป็นค่าใหม่ที่เราต้องการ

เปลี่ยนค่าจากไอดีที่ต้องการ
เปลี่ยนเสร็จแล้ว

DELETE

ทำได้โดย ส่ง request ไปที่ /stormtrooper/delete-stormtrooper/<id> จากนั้น backend จะส่ง response มาเป็น json object message ตาม <id> ที่โดนลบไป

สั่งลบ "Bon Jovi"
หลังจากลบไปแล้ว เรียกดูข้อมูลอีกที ก็พบว่า "Bon Jovi" หายไปแล้ว

Summary

จากกระบวนวิธีการสร้าง api เพื่อให้ client ได้เชื่อมต่อกับฐานข้อมูลเพื่อ ส่ง รับ แก้ไข และลบข้อมูลนั้น เป็นพื้นฐานที่ใช้ต่อยอดไปยังการสร้างแอพลิเคชั่นที่ซับซ้อนต่อไปในอนาคตได้ ซึ่งบทความนี้อาจจะไม่ได้แสดงวิธีการเขียนอย่างละเอียด เพียงแค่เป็นแนวทางและเป็นบันทึกความทรงจำไว้เท่านั้น

References

Building a Simple CRUD app with Node, Express, and MongoDB
React CRUD: How To Build React 16 CRUD Example
MERN STACK Failed to load resource: net::ERR_CONNECTION_REFUSED
Building a Restful CRUD API with Node.js, Express and MongoDB

Cover Image

Building a Simple CRUD app with Node, Express, and MongoDB