Skip to content

รูปแบบสถาปัตยกรรม API (API Architecture Styles)

API Architecture Styles

การออกแบบ API มีหลายรูปแบบสถาปัตยกรรมที่แตกต่างกัน แต่ละรูปแบบมีจุดแข็งและข้อจำกัดที่แตกต่างกัน การเลือกใช้สถาปัตยกรรมที่เหมาะสมจะช่วยให้การพัฒนาระบบมีประสิทธิภาพและตอบโจทย์ความต้องการของธุรกิจได้ดียิ่งขึ้น

RESTful API

เป็นสถาปัตยกรรม API ที่ใช้กันอย่างแพร่หลายที่สุด ใช้หลักการของ HTTP Method (GET, POST, PUT, DELETE) ในการจัดการทรัพยากร (Resources) ผ่าน URL โดยมีโครงสร้างที่เป็นมาตรฐาน ง่ายต่อการเข้าใจ และใช้งาน RESTful API มักส่งข้อมูลในรูปแบบ JSON หรือ XML ซึ่งเป็นรูปแบบที่ทั้ง Human และ Machine อ่านได้ง่าย

ตัวอย่างสถานการณ์: แอปพลิเคชันที่ต้องการดึงข้อมูลผู้ใช้ แก้ไขข้อมูล หรือลบข้อมูล สามารถใช้ RESTful API ในการจัดการได้

http
GET /users/1 HTTP/1.1
Host: example.com
ts
import express from 'express';
const app = express();

app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  // ดึงข้อมูลผู้ใช้จากฐานข้อมูล
  res.json({ id: userId, name: 'John Doe', email: '[email protected]' });
});

app.listen(3000, () => {
  console.log('REST API server running on port 3000');
});
ts
import { Controller, Get, Param } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    return { id, name: 'John Doe', email: '[email protected]' };
  }
}

REST API with Next.js, Nuxt.js and Nitro

Next.js และ Nuxt.js เป็นเฟรมเวิร์กที่รองรับการสร้าง REST API ในตัว:

Next.js API Route
ts
// pages/api/user.ts
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    // Handle POST request
    res.status(200).json({ name: 'John Doe' })
  } else {
    // Handle other HTTP methods
    res.setHeader('Allow', ['POST'])
    res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}
Nuxt.js Server API
ts
// server/api/user.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { 
    status: 'success',
    data: body
  }
})

Nitro เป็นเซิร์ฟเวอร์เฟรมเวิร์กที่ใช้ใน Nuxt.js 3:

Nitro API
ts
// server/api/user.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { 
    status: 'success',
    data: body
  }
})

GraphQL

เป็นภาษาสำหรับการ "สอบถาม" (Query Language) ข้อมูลผ่าน API ที่มีความยืดหยุ่นสูง จุดเด่นคือ Client (ผู้เรียกใช้ API) สามารถระบุโครงสร้างข้อมูลที่ต้องการได้อย่างเฉพาะเจาะจง ทำให้ได้รับข้อมูลเท่าที่จำเป็นจริงๆ ไม่มากหรือน้อยเกินไป ช่วยแก้ปัญหาการดึงข้อมูลที่ไม่จำเป็น (Over-fetching) หรือต้องเรียก API หลายครั้งเพื่อให้ได้ข้อมูลครบ (Under-fetching) ที่มักพบใน RESTful API

ตัวอย่างสถานการณ์: สมมติว่าแอปพลิเคชันต้องการแสดงแค่ชื่อและอีเมลของผู้ใช้ แทนที่จะดึงข้อมูลทั้งหมด (ที่อยู่, ประวัติการสั่งซื้อ ฯลฯ) เหมือนใน REST ทั่วไป GraphQL ทำให้ Client ร้องขอแค่ name และ email ได้

graphql
query {
  user(id: 1) {
    id
    name
    email
  }
}
ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const typeDefs = `
  type User {
    id: ID!
    name: String!
    email: String!
  }

  type Query {
    user(id: ID!): User
  }
`;

const resolvers = {
  Query: {
    user: (_, { id }) => {
      // ดึงข้อมูลผู้ใช้จากฐานข้อมูล
      return { id, name: 'John Doe', email: '[email protected]' };
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
startStandaloneServer(server, { listen: { port: 4000 } });
ts
import { Args, Query, Resolver } from '@nestjs/graphql';
import { User } from './user.model';

@Resolver(() => User)
export class UserResolver {
  @Query(() => User)
  async user(@Args('id') id: string) {
    return { id, name: 'John Doe', email: '[email protected]' };
  }
}

WebSocket with Nitro

Nitro รองรับ WebSocket โดยใช้ h3 และ ws:

Nitro WebSocket
ts
// server/websocket.ts
import { createApp, createRouter, eventHandler, toNodeListener } from 'h3'
import { WebSocketServer } from 'ws'

const app = createApp()
const router = createRouter()

const wss = new WebSocketServer({ noServer: true })

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    console.log('received: %s', message)
    ws.send(`Echo: ${message}`)
  })
})

router.get('/ws', eventHandler((event) => {
  // Handle WebSocket upgrade
  if (event.node.req.headers.upgrade === 'websocket') {
    const { socket, head } = event.node.req
    wss.handleUpgrade(event.node.req, socket, head, (ws) => {
      wss.emit('connection', ws, event.node.req)
    })
  }
}))

app.use(router)
export default toNodeListener(app)

gRPC

เป็น Framework ประสิทธิภาพสูงสำหรับการสื่อสารระหว่าง Service ต่างๆ พัฒนาโดย Google ใช้หลักการ Remote Procedure Call (RPC) คือการเรียกใช้ฟังก์ชันที่อยู่บน Server อื่นเสมือนเป็นฟังก์ชันในเครื่องตัวเอง gRPC ใช้ Protocol Buffers ซึ่งเป็นวิธีบีบอัดข้อมูลที่มีประสิทธิภาพ ทำให้การสื่อสารรวดเร็วและใช้ Bandwidth น้อยกว่า REST/JSON เหมาะสำหรับระบบ Microservices ที่ต้องการการสื่อสารภายในที่รวดเร็ว และรองรับการส่งข้อมูลแบบต่อเนื่อง (Streaming) ได้ดี

ตัวอย่างสถานการณ์: ระบบ Microservices หลายตัวต้องคุยกันภายในเพื่อประมวลผลคำสั่งซื้อ การใช้ gRPC จะช่วยให้การสื่อสารรวดเร็วและมีประสิทธิภาพกว่าการใช้ REST API

proto
service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}
ts
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

const packageDefinition = protoLoader.loadSync('user.proto');
const userProto = grpc.loadPackageDefinition(packageDefinition);

const server = new grpc.Server();
server.addService(userProto.UserService.service, {
  getUser: (call, callback) => {
    const userId = call.request.id;
    // ดึงข้อมูลผู้ใช้จากฐานข้อมูล
    callback(null, { id: userId, name: 'John Doe', email: '[email protected]' });
  }
});

server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
  server.start();
  console.log('gRPC server running on port 50051');
});
ts
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

interface UserById {
  id: string;
}

interface User {
  id: string;
  name: string;
  email: string;
}

@Controller()
export class UserController {
  @GrpcMethod('UserService', 'GetUser')
  getUser(data: UserById): User {
    return { id: data.id, name: 'John Doe', email: '[email protected]' };
  }
}

WebSocket

เป็นโปรโตคอลที่ออกแบบมาเพื่อการสื่อสารแบบ "สองทาง" (Bi-directional) และ "ทันที" (Real-time) ระหว่าง Client และ Server ผ่านการเชื่อมต่อเพียงครั้งเดียว เมื่อเชื่อมต่อแล้ว ทั้ง Client และ Server สามารถส่งข้อมูลหากันได้ตลอดเวลาโดยไม่ต้องร้องขอใหม่ทุกครั้ง เหมาะสำหรับแอปพลิเคชันที่ต้องการการอัปเดตข้อมูลแบบสดๆ

ตัวอย่างสถานการณ์: แอปพลิเคชันแชท, การแสดงผลราคาหุ้นแบบ Real-time, หรือเกมออนไลน์ที่ต้องการการตอบสนองทันที

plaintext
ws://example.com/socket
ts
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('Client connected');
  
  // รับข้อความจาก Client
  ws.on('message', (message) => {
    console.log(`Received: ${message}`);
    
    // ส่งข้อความกลับไปยัง Client
    ws.send(`Echo: ${message}`);
  });
  
  // ส่งข้อมูลแบบ real-time ทุกๆ 2 วินาที
  const interval = setInterval(() => {
    ws.send(JSON.stringify({ time: new Date().toISOString() }));
  }, 2000);
  
  ws.on('close', () => {
    clearInterval(interval);
  });
});
ts
import { createServer } from 'http';
import { Server } from 'socket.io';

const httpServer = createServer();
const io = new Server(httpServer);

io.on('connection', (socket) => {
  console.log('Client connected');
  
  // รับเหตุการณ์จาก Client
  socket.on('chat message', (msg) => {
    console.log(`Message: ${msg}`);
    // ส่งข้อความไปยังทุก Client ที่เชื่อมต่อ
    io.emit('chat message', msg);
  });
  
  socket.on('disconnect', () => {
    console.log('Client disconnected');
  });
});

httpServer.listen(3000, () => {
  console.log('WebSocket server running on port 3000');
});

Webhook

เป็นกลไกที่ Server หนึ่งจะ "แจ้งเตือน" อีก Server หนึ่งโดยอัตโนมัติเมื่อมีเหตุการณ์ (Event) บางอย่างเกิดขึ้น โดยการส่งข้อมูลผ่าน HTTP POST ไปยัง URL ที่กำหนดไว้ล่วงหน้า (Webhook URL) ไม่ใช่การที่ Client ร้องขอข้อมูล แต่เป็นการที่ Server "ผลัก" ข้อมูลออกไปเมื่อมีอะไรใหม่ๆ เกิดขึ้น

ตัวอย่างสถานการณ์: ระบบ E-commerce แจ้งเตือนระบบบัญชีเมื่อมีการชำระเงินสำเร็จ, หรือ GitHub แจ้งเตือนเซิร์ฟเวอร์ CI/CD เมื่อมี Code ใหม่ถูก Push เข้ามา

http
POST https://example.com/webhook
Content-Type: application/json

{
  "event": "user_created",
  "data": { "id": 1, "name": "John Doe" }
}
ts
import axios from 'axios';

async function triggerWebhook(event: string, data: any) {
  const webhookUrl = 'https://example.com/webhook';
  
  try {
    await axios.post(webhookUrl, {
      event,
      data,
      timestamp: new Date().toISOString()
    });
    console.log(`Webhook triggered for event: ${event}`);
  } catch (error) {
    console.error('Failed to trigger webhook:', error);
  }
}

// ตัวอย่างการใช้งาน
triggerWebhook('user_created', { id: 1, name: 'John Doe' });