ไทย

เรียนรู้วิธีการสร้าง API endpoint ที่ทรงพลังด้วย Next.js Route Handlers คู่มือนี้ครอบคลุมทุกอย่างตั้งแต่การตั้งค่าพื้นฐานไปจนถึงเทคนิคขั้นสูง พร้อมตัวอย่างที่ใช้งานได้จริงและแนวทางปฏิบัติที่ดีที่สุด

Next.js Route Handlers: คู่มือฉบับสมบูรณ์สำหรับการสร้าง API Endpoint

Next.js ได้ปฏิวัติวิธีการสร้างเว็บแอปพลิเคชันด้วยฟีเจอร์ที่ทรงพลัง เช่น server-side rendering, static site generation และล่าสุดคือ Route Handlers ซึ่งเป็นวิธีที่ยืดหยุ่นและมีประสิทธิภาพในการสร้าง API endpoint ภายในแอปพลิเคชัน Next.js ของคุณโดยตรง คู่มือนี้จะสำรวจแนวคิดของ Route Handlers, ประโยชน์ของมัน และวิธีใช้งานอย่างมีประสิทธิภาพเพื่อสร้าง API ที่แข็งแกร่ง

Next.js Route Handlers คืออะไร?

Route Handlers คือฟังก์ชันที่กำหนดไว้ในไดเรกทอรี app ของโปรเจกต์ Next.js ซึ่งทำหน้าที่จัดการกับ HTTP request ที่เข้ามา ซึ่งแตกต่างจากแนวทางเดิมของ pages/api (ที่ใช้ API Routes) โดย Route Handlers นำเสนอวิธีที่คล่องตัวและยืดหยุ่นกว่าในการกำหนด API endpoint ควบคู่ไปกับ React component ของคุณ โดยพื้นฐานแล้วมันคือ serverless functions ที่ทำงานบน edge หรือสภาพแวดล้อมเซิร์ฟเวอร์ที่คุณเลือก

ลองนึกภาพว่า Route Handlers คือส่วนตรรกะหลังบ้าน (backend logic) ของแอปพลิเคชัน Next.js ของคุณ ซึ่งรับผิดชอบในการประมวลผลคำขอ (request), การโต้ตอบกับฐานข้อมูล และการส่งคืนการตอบกลับ (response)

ประโยชน์ของการใช้ Route Handlers

การตั้งค่าโปรเจกต์ Next.js ของคุณ

ก่อนที่จะเริ่มใช้งาน Route Handlers ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าโปรเจกต์ Next.js ที่มีไดเรกทอรี app แล้ว หากคุณกำลังเริ่มโปรเจกต์ใหม่ ให้ใช้คำสั่งต่อไปนี้:

npx create-next-app@latest my-nextjs-app

เลือกไดเรกทอรี app ในระหว่างขั้นตอนการตั้งค่าเพื่อเปิดใช้งานระบบ routing ใหม่

การสร้าง Route Handler แรกของคุณ

เรามาสร้าง API endpoint ง่ายๆ ที่ส่งคืนการตอบกลับแบบ JSON กัน สร้างไดเรกทอรีใหม่ภายในไดเรกทอรี app ตัวอย่างเช่น /app/api/hello จากนั้นภายในไดเรกทอรีนี้ ให้สร้างไฟล์ชื่อ route.ts (หรือ route.js หากคุณไม่ได้ใช้ TypeScript)

นี่คือโค้ดสำหรับ Route Handler แรกของคุณ:

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
}

คำอธิบาย:

ตอนนี้คุณสามารถเข้าถึง endpoint นี้ได้โดยการไปที่ /api/hello ในเบราว์เซอร์ของคุณ หรือใช้เครื่องมืออย่าง curl หรือ Postman

การจัดการ HTTP Methods ต่างๆ

Route Handlers รองรับ HTTP methods ที่หลากหลาย เช่น GET, POST, PUT, DELETE, PATCH และ OPTIONS คุณสามารถกำหนดฟังก์ชันแยกต่างหากสำหรับแต่ละเมธอดภายในไฟล์ route.ts เดียวกันได้

// app/api/users/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 // ตรรกะในการดึงข้อมูลผู้ใช้ทั้งหมดจากฐานข้อมูล
 const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // ข้อมูลตัวอย่าง
 return NextResponse.json(users);
}

export async function POST(request: Request) {
 const data = await request.json(); // แยกวิเคราะห์ request body เป็น JSON
 // ตรรกะในการสร้างผู้ใช้ใหม่ในฐานข้อมูลโดยใช้ 'data'
 const newUser = { id: 3, name: data.name, email: data.email }; // ตัวอย่าง
 return NextResponse.json(newUser, { status: 201 }); // ส่งคืนผู้ใช้ใหม่พร้อมกับ status code 201 Created
}

คำอธิบาย:

การเข้าถึงข้อมูลคำขอ (Request Data)

อ็อบเจกต์ request ช่วยให้สามารถเข้าถึงข้อมูลต่างๆ เกี่ยวกับคำขอที่เข้ามาได้ รวมถึง headers, query parameters และ request body

Headers

คุณสามารถเข้าถึง request headers ได้โดยใช้คุณสมบัติ request.headers:

export async function GET(request: Request) {
 const userAgent = request.headers.get('user-agent');
 console.log('User Agent:', userAgent);
 return NextResponse.json({ userAgent });
}

Query Parameters

ในการเข้าถึง query parameters คุณสามารถใช้ constructor URL:

export async function GET(request: Request) {
 const url = new URL(request.url);
 const searchParams = new URLSearchParams(url.search);
 const id = searchParams.get('id');
 console.log('ID:', id);
 return NextResponse.json({ id });
}

Request Body

สำหรับคำขอประเภท POST, PUT และ PATCH คุณสามารถเข้าถึง request body ได้โดยใช้เมธอด request.json() หรือ request.text() ขึ้นอยู่กับ content type

export async function POST(request: Request) {
 const data = await request.json();
 console.log('Data:', data);
 return NextResponse.json({ receivedData: data });
}

การส่งคืนการตอบกลับ (Responses)

อ็อบเจกต์ NextResponse ใช้ในการสร้างการตอบกลับของ API ซึ่งมีเมธอดหลายอย่างสำหรับการตั้งค่า headers, status codes และ response bodies

การตอบกลับแบบ JSON

ใช้เมธอด NextResponse.json() เพื่อส่งคืนการตอบกลับแบบ JSON:

return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });

การตอบกลับแบบข้อความ (Text)

ใช้ constructor new Response() เพื่อส่งคืนการตอบกลับแบบข้อความธรรมดา:

return new Response('Hello, world!', { status: 200, headers: { 'Content-Type': 'text/plain' } });

การเปลี่ยนเส้นทาง (Redirects)

ใช้ NextResponse.redirect() เพื่อเปลี่ยนเส้นทางผู้ใช้ไปยัง URL อื่น:

import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.redirect(new URL('/new-location', request.url));
}

การตั้งค่า Headers

คุณสามารถตั้งค่า custom headers ได้โดยใช้ตัวเลือก headers ใน NextResponse.json() หรือ new Response():

return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });

การผสานรวม Middleware

Middleware ช่วยให้คุณสามารถรันโค้ดก่อนที่คำขอจะถูกจัดการโดย Route Handler ของคุณ ซึ่งมีประโยชน์สำหรับการยืนยันตัวตน, การอนุญาต, การบันทึกข้อมูล (logging) และข้อกังวลอื่นๆ ที่ต้องทำข้ามส่วน (cross-cutting concerns)

ในการสร้าง middleware ให้สร้างไฟล์ชื่อ middleware.ts (หรือ middleware.js) ในไดเรกทอรี app หรือไดเรกทอรีย่อยใดๆ middleware จะมีผลกับทุก routes ภายในไดเรกทอรีนั้นและไดเรกทอรีย่อยของมัน

// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth-token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/protected/:path*'], // ใช้ middleware นี้กับ path ที่ขึ้นต้นด้วย /protected/
};

คำอธิบาย:

การจัดการข้อผิดพลาด (Error Handling)

การจัดการข้อผิดพลาดที่เหมาะสมเป็นสิ่งสำคัญอย่างยิ่งในการสร้าง API ที่แข็งแกร่ง คุณสามารถใช้บล็อก try...catch เพื่อจัดการกับข้อยกเว้นและส่งคืนการตอบกลับข้อผิดพลาดที่เหมาะสม

export async function GET(request: Request) {
 try {
 // จำลองข้อผิดพลาด
 throw new Error('Something went wrong!');
 } catch (error: any) {
 console.error('Error:', error);
 return NextResponse.json({ error: error.message }, { status: 500 });
 }
}

คำอธิบาย:

การตอบกลับแบบสตรีมมิ่ง (Streaming Responses)

Route Handlers รองรับการตอบกลับแบบสตรีมมิ่ง ซึ่งช่วยให้คุณสามารถส่งข้อมูลไปยังไคลเอนต์ทีละส่วนได้ นี่เป็นประโยชน์อย่างยิ่งสำหรับชุดข้อมูลขนาดใหญ่หรือกระบวนการที่ใช้เวลานาน

import { Readable } from 'stream';
import { NextResponse } from 'next/server';

async function* generateData() {
 for (let i = 0; i < 10; i++) {
 await new Promise(resolve => setTimeout(resolve, 500)); // จำลองความล่าช้า
 yield `Data chunk ${i}\n`;
 }
}

export async function GET(request: Request) {
 const readableStream = Readable.from(generateData());

 return new Response(readableStream, {
 headers: { 'Content-Type': 'text/plain; charset=utf-8' },
 });
}

คำอธิบาย:

การยืนยันตัวตนและการอนุญาต (Authentication and Authorization)

การรักษาความปลอดภัยของ API endpoint ของคุณเป็นสิ่งสำคัญ คุณสามารถใช้การยืนยันตัวตนและการอนุญาตโดยใช้ middleware หรือโดยตรงภายใน Route Handlers ของคุณ

การยืนยันตัวตน (Authentication)

การยืนยันตัวตนเป็นการตรวจสอบตัวตนของผู้ใช้ที่ส่งคำขอ วิธีการยืนยันตัวตนที่พบบ่อย ได้แก่:

นี่คือตัวอย่างการยืนยันตัวตนด้วย JWT โดยใช้ middleware:

// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';

const secret = process.env.JWT_SECRET || 'your-secret-key'; // แทนที่ด้วย secret ที่สร้างขึ้นแบบสุ่มและแข็งแกร่ง

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth-token')?.value;

 if (!token) {
 return NextResponse.json({ message: 'Authentication required' }, { status: 401 });
 }

 try {
 jwt.verify(token, secret);
 return NextResponse.next();
 } catch (error) {
 return NextResponse.json({ message: 'Invalid token' }, { status: 401 });
 }
}

export const config = {
 matcher: ['/api/protected/:path*'],
};

การอนุญาต (Authorization)

การอนุญาตเป็นการกำหนดว่าผู้ใช้ได้รับอนุญาตให้เข้าถึงทรัพยากรใด ซึ่งโดยทั่วไปจะขึ้นอยู่กับบทบาท (roles) หรือสิทธิ์ (permissions)

คุณสามารถใช้การอนุญาตภายใน Route Handlers ของคุณได้โดยการตรวจสอบบทบาทหรือสิทธิ์ของผู้ใช้ และส่งคืนข้อผิดพลาดหากพวกเขาไม่มีสิทธิ์เข้าถึง

// app/api/admin/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 // สมมติว่าคุณมีฟังก์ชันในการดึงบทบาทของผู้ใช้จากโทเค็นหรือเซสชัน
 const userRole = await getUserRole(request);

 if (userRole !== 'admin') {
 return NextResponse.json({ message: 'Unauthorized' }, { status: 403 });
 }

 // ตรรกะในการดึงข้อมูลแอดมิน
 const adminData = { message: 'Admin data' };
 return NextResponse.json(adminData);
}

async function getUserRole(request: Request): Promise {
 // แทนที่ด้วยตรรกะจริงของคุณในการดึงบทบาทของผู้ใช้จากคำขอ
 // ซึ่งอาจเกี่ยวข้องกับการตรวจสอบโทเค็น JWT หรือการตรวจสอบเซสชัน
 return 'admin'; // ตัวอย่าง: บทบาทที่กำหนดไว้ตายตัวเพื่อการสาธิต
}

การปรับใช้ (Deploying) Route Handlers

Route Handlers ถูกปรับใช้เป็น serverless functions บนผู้ให้บริการโฮสติ้งที่คุณเลือก Next.js รองรับแพลตฟอร์มการปรับใช้ที่หลากหลาย รวมถึง Vercel, Netlify, AWS และอื่นๆ

สำหรับ Vercel การปรับใช้นั้นง่ายเพียงแค่เชื่อมต่อ Git repository ของคุณกับ Vercel และ push โค้ดของคุณ Vercel จะตรวจจับโปรเจกต์ Next.js ของคุณโดยอัตโนมัติและปรับใช้ Route Handlers ของคุณเป็น serverless functions

เทคนิคขั้นสูง

Edge Functions

Route Handlers สามารถปรับใช้เป็น Edge Functions ซึ่งจะทำงานที่ขอบของ CDN ใกล้กับผู้ใช้ของคุณมากขึ้น สิ่งนี้สามารถลดความหน่วงและปรับปรุงประสิทธิภาพได้อย่างมาก

ในการปรับใช้ Route Handler เป็น Edge Function ให้เพิ่ม edge runtime ไปยังไฟล์ route.ts ของคุณ:

export const runtime = 'edge';

import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.json({ message: 'Hello from the Edge!' });
}

Server Actions

Server Actions ช่วยให้คุณสามารถเรียกใช้โค้ดฝั่งเซิร์ฟเวอร์ได้โดยตรงจาก React component ของคุณ Route Handlers และ Server Actions ทำงานร่วมกันได้อย่างราบรื่น ช่วยให้คุณสร้างแอปพลิเคชันที่ซับซ้อนได้อย่างง่ายดาย

นี่คือตัวอย่างการใช้ Server Action เพื่อเรียก Route Handler:

// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';

async function handleSubmit(data: FormData) {
 'use server';

 const name = data.get('name');
 const email = data.get('email');

 const response = await fetch('/api/users', {
 method: 'POST',
 body: JSON.stringify({ name, email }),
 });

 if (response.ok) {
 router.refresh(); // รีเฟรชหน้าเพื่อสะท้อนการเปลี่ยนแปลง
 }
}

export default function MyComponent() {
 const router = useRouter();

 return (
 




); }

การแคช (Caching)

การแคชสามารถปรับปรุงประสิทธิภาพของ API endpoint ของคุณได้อย่างมาก คุณสามารถใช้ header Cache-Control เพื่อควบคุมวิธีที่การตอบกลับของคุณถูกแคชโดยเบราว์เซอร์และ CDN

return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });

ตัวอย่างนี้ตั้งค่า header Cache-Control เป็น public, max-age=3600 ซึ่งจะบอกให้เบราว์เซอร์และ CDN แคชการตอบกลับเป็นเวลาหนึ่งชั่วโมง

แนวทางปฏิบัติที่ดีที่สุด (Best Practices)

ตัวอย่างการใช้งานจริง

นี่คือตัวอย่างการใช้งานจริงบางส่วนที่สามารถใช้ Route Handlers ได้:

ตัวอย่าง E-commerce ระหว่างประเทศ: Route Handler ที่ใช้ดึงราคาสินค้าตามประเทศของผู้ใช้ Endpoint สามารถใช้ตำแหน่งทางภูมิศาสตร์ของคำขอ (ที่ได้จาก IP address) เพื่อระบุตำแหน่งของผู้ใช้และส่งคืนราคาในสกุลเงินที่เหมาะสม ซึ่งช่วยสร้างประสบการณ์การช็อปปิ้งที่ปรับให้เข้ากับท้องถิ่น

ตัวอย่างการยืนยันตัวตนระดับโลก: Route Handler ที่ใช้การยืนยันตัวตนแบบหลายปัจจัย (MFA) สำหรับผู้ใช้ทั่วโลก ซึ่งอาจเกี่ยวข้องกับการส่งรหัส SMS หรือใช้แอปยืนยันตัวตน โดยเคารพกฎระเบียบด้านความเป็นส่วนตัวและโครงสร้างพื้นฐานด้านโทรคมนาคมของแต่ละภูมิภาค

การส่งมอบเนื้อหาหลายภาษา: Route Handler ที่ส่งมอบเนื้อหาในภาษาที่ผู้ใช้ต้องการ ซึ่งสามารถกำหนดได้จาก header `Accept-Language` ในคำขอ ตัวอย่างนี้เน้นให้เห็นถึงความจำเป็นในการเข้ารหัส UTF-8 ที่เหมาะสมและการรองรับภาษาที่เขียนจากขวาไปซ้ายตามความเหมาะสม

บทสรุป

Next.js Route Handlers เป็นวิธีที่ทรงพลังและยืดหยุ่นในการสร้าง API endpoint ภายในแอปพลิเคชัน Next.js ของคุณโดยตรง ด้วยการใช้ประโยชน์จาก Route Handlers คุณสามารถสร้าง API ที่แข็งแกร่งได้อย่างง่ายดาย, จัดวางตรรกะหลังบ้านของคุณไว้คู่กับ React component และใช้ประโยชน์จากฟีเจอร์ต่างๆ เช่น middleware, streaming และ Edge Functions

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