NodeJS, Express & Database
NodeJS ทำให้รัน JavaScript นอกเบราว์เซอร์ได้ — เขียน front-end และ back-end ด้วยภาษาเดียวกัน
NodeJS คืออะไร
- รัน JavaScript ฝั่ง server ได้ — จัดการไฟล์ ฐานข้อมูล และ network
- ใช้ภาษาเดียวกับฝั่ง client จึงพัฒนาต่อเนื่องได้ง่าย
- งานฝั่ง server มี 2 แบบ: SSR (สร้าง HTML ส่งกลับทั้งหน้า) และ API (ส่งข้อมูล JSON)
Express เป็น framework ที่ช่วยสร้าง route และจัดการ HTTP
npm & npx
- ติดตั้ง NodeJS แล้วจะได้ npm (node package manager) มาด้วย
- npm ใช้ติดตั้ง library เพิ่มเติม ทั้งฝั่ง client และ server
npxใช้รันคำสั่งของ package โดยไม่ต้องติดตั้งถาวร
npm init # สร้างโปรเจกต์ node ใหม่ (ได้ package.json) npm install express # ติดตั้ง express npm install axios # ติดตั้ง axios npm install -g typescript# ติดตั้งแบบ global
สร้าง server ตัวแรก
const express = require('express');
const app = express();
app.get('/', (req, res) => { // สร้าง route ที่ localhost/
res.send('Hello World!');
});
app.listen(3000, () => { // รัน server ที่ port 3000
console.log('Example app listening at http://localhost:3000');
});
require vs import
require() | import ... from | |
|---|---|---|
| เป็นของ | NodeJS (CommonJS) | ECMAScript module |
| ใช้กับ | module ที่จัดการระบบ เช่น fs, mariadb | โมดูลทั่วไป เช่นในโค้ด React |
| ตัวอย่าง | const fs = require('fs'); | import person from './person.js'; |
library ที่ทำงานกับระบบไฟล์หรือฐานข้อมูล (
fs, mariadb) ใช้ได้เฉพาะฝั่ง server
จึงไม่เห็นคำสั่ง require ในโค้ด Reactfetch ฝั่ง server
app.get('/', async (req, res) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
res.json(data); // ส่งต่อให้ client
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
หรือใช้ axios ซึ่งกำหนด method ได้สะดวกกว่า:
const axios = require('axios');
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
const data = response.data;
ประเภทฐานข้อมูล
| ประเภท | ลักษณะ | ตัวอย่าง |
|---|---|---|
| Structured (relational) | ตารางมี schema ชัดเจน | MariaDB, MySQL, MS-SQL |
| Semi-structured | เก็บเป็น JSON / XML | MongoDB |
| Unstructured | ไฟล์ดิบ | PDF, Word |
CRUD กับ MariaDB
npm install express mariadb
const express = require('express');
const mariadb = require('mariadb');
const app = express();
const pool = mariadb.createPool({
host: 'localhost',
user: '<username>',
password: '<password>',
database: '<database>',
connectionLimit: 5 // จำนวนการเชื่อมต่อสูงสุด
});
app.use(express.json());
Read — GET
app.get('/data', async (req, res) => {
let conn;
try {
conn = await pool.getConnection();
const rows = await conn.query("SELECT * FROM <table>");
res.json(rows);
} catch (err) {
console.error(err);
res.status(500).send('Internal Server Error');
} finally {
if (conn) conn.end(); // คืน connection กลับ pool เสมอ
}
});
Create — POST
app.post('/data', async (req, res) => {
let conn;
try {
conn = await pool.getConnection();
const { field1, field2 } = req.body;
await conn.query("INSERT INTO <table> (field1, field2) VALUES (?, ?)", [field1, field2]);
res.send('Data inserted successfully');
} catch (err) {
res.status(500).send('Internal Server Error');
} finally {
if (conn) conn.end();
}
});
Update — PUT / Delete — DELETE
app.put('/data/:id', async (req, res) => {
const { field1, field2 } = req.body;
const id = req.params.id;
await conn.query("UPDATE <table> SET field1 = ?, field2 = ? WHERE id = ?", [field1, field2, id]);
});
app.delete('/data/:id', async (req, res) => {
const id = req.params.id;
await conn.query("DELETE FROM <table> WHERE id = ?", [id]);
});
สังเกตว่าใช้ placeholder
? แล้วส่งค่าเป็น array —
นี่คือวิธีป้องกัน SQL Injection ห้ามต่อสตริงเอาค่าจากผู้ใช้ยัดเข้า query ตรง ๆกรณี JOIN แล้วชื่อ field ซ้ำกัน ให้ใส่
AS ตั้งชื่อใหม่ เช่น
SELECT users.id AS id, orders.id AS order_idMongoDB & mongoose
MongoDB > database > collection > document
| MongoDB | เทียบกับ relational |
|---|---|
| collection | table |
| document (BSON) | row |
npm install express mongoose body-parser
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/<database>');
// ต้องสร้าง schema ก่อน (ODM — Object Document Mapping)
const itemSchema = new mongoose.Schema({
name: String,
description: String
});
const Item = mongoose.model('Item', itemSchema);
// CRUD
const items = await Item.find({}); // อ่านทั้งหมด
const newItem = new Item(req.body); await newItem.save(); // เพิ่ม
await Item.findByIdAndUpdate(id, req.body, { new: true }); // แก้
await Item.findByIdAndDelete(id); // ลบ
ORM (Prisma)
ORM (Object Relational Mapping) = ออกแบบที่ model อย่างเดียว ไม่ต้องเขียน schema ในฐานข้อมูลเอง
npm install -g prisma
npx prisma init
npx prisma db push # สร้าง/แก้ตารางตาม model
// schema.prisma
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
authorId Int
author User @relation(fields: [authorId], references: [id])
}
const users = await prisma.user.findMany();
const newUser = await prisma.user.create({ data: { name: 'John', email: 'john@example.com' } });
const updated = await prisma.user.update({ where: { id: 1 }, data: { name: 'Updated John' } });
const deleted = await prisma.user.delete({ where: { id: 1 } });
| วิธี join | แนวคิด |
|---|---|
include: { posts: true } | Eager Loading — โหลด model ที่เกี่ยวข้องมาพร้อมกัน |
select: { name: true, posts: true } | Lazy Loading — เลือกโหลดเฉพาะที่ต้องการ |
ข้อจำกัด: schema ของ Prisma ทำ extends สืบทอด object โดยตรงไม่ได้ — ต้องสร้าง model ใหม่แล้วแปลงเอง
Add-on: TypeScript
TypeScript (TS) = ภาษาที่ต่อยอดจาก JavaScript โดยเพิ่ม การกำหนดชนิดข้อมูล
| ปัญหาของ JS | TS แก้อย่างไร |
|---|---|
| Implicit conversion (แปลงชนิดอัตโนมัติ) ทำให้เกิด data type error | ตรวจชนิดตั้งแต่ตอน compile |
ขาดคุณสมบัติ OOP บางอย่าง เช่น interface | มี interface และ generic <T> |
npm install -g typescript tsc -v # เช็คเวอร์ชัน tsc app.ts # compile เป็น app.js tsc --init # สร้าง tsconfig.json
function add(a: number, b: number) {
return a + b;
}
console.log(add(true, 2)); // tsc app.ts → error ตั้งแต่ตอน compile
interface LabeledValue {
label: string;
}
| ค่าใน tsconfig.json | ความหมาย |
|---|---|
rootDir | โฟลเดอร์ต้นทางของโปรเจกต์ TS |
outDir | โฟลเดอร์ปลายทางที่ compile ออกมาเป็น JS (ค่าปริยาย ./dist/) |
include | โฟลเดอร์ไหนบ้างที่จะ compile เช่น ["src"] |
เบราว์เซอร์ไม่รู้จัก TypeScript — ต้อง compile เป็น JavaScript ก่อนเสมอ