ไทย

สำรวจ Next.js API Routes และปลดล็อกความสามารถในการพัฒนา full-stack ภายในแอปพลิเคชัน React ของคุณ เรียนรู้รูปแบบ แนวทางปฏิบัติที่ดีที่สุด และกลยุทธ์การ deployment

Next.js API Routes: รูปแบบการพัฒนา Full-Stack

Next.js ได้ปฏิวัติการพัฒนา React โดยมอบเฟรมเวิร์กที่แข็งแกร่งสำหรับการสร้างเว็บแอปพลิเคชันที่มีประสิทธิภาพและปรับขนาดได้ หนึ่งในฟีเจอร์ที่สำคัญคือ API Routes ซึ่งช่วยให้นักพัฒนาสามารถสร้างฟังก์ชันการทำงานของแบ็กเอนด์ได้โดยตรงภายในโปรเจกต์ Next.js ของตน แนวทางนี้ช่วยเพิ่มความคล่องตัวในการพัฒนา ทำให้การ deployment ง่ายขึ้น และปลดล็อกความสามารถด้าน full-stack ที่ทรงพลัง

Next.js API Routes คืออะไร?

Next.js API Routes คือ serverless functions ที่เขียนขึ้นโดยตรงภายในไดเรกทอรี /pages/api ของคุณ แต่ละไฟล์ในไดเรกทอรีนี้จะกลายเป็น API endpoint โดยทำการ routing คำขอ HTTP ไปยังฟังก์ชันที่สอดคล้องกันโดยอัตโนมัติ ซึ่งช่วยลดความจำเป็นในการมีเซิร์ฟเวอร์แบ็กเอนด์แยกต่างหาก ทำให้สถาปัตยกรรมแอปพลิเคชันของคุณง่ายขึ้นและลดภาระในการดำเนินงาน

ลองนึกภาพว่ามันคือ serverless functions ขนาดเล็กที่อยู่ภายในแอป Next.js ของคุณ มันตอบสนองต่อคำขอ HTTP เช่น GET, POST, PUT, DELETE และสามารถโต้ตอบกับฐานข้อมูล, API ภายนอก และทรัพยากรฝั่งเซิร์ฟเวอร์อื่นๆ ได้ ที่สำคัญคือ มันทำงานบนเซิร์ฟเวอร์เท่านั้น ไม่ใช่ในเบราว์เซอร์ของผู้ใช้ จึงมั่นใจได้ในความปลอดภัยของข้อมูลที่ละเอียดอ่อน เช่น API keys

ประโยชน์หลักของ API Routes

เริ่มต้นใช้งาน API Routes

การสร้าง API route ใน Next.js นั้นตรงไปตรงมา เพียงแค่สร้างไฟล์ใหม่ภายในไดเรกทอรี /pages/api ชื่อไฟล์จะเป็นตัวกำหนดเส้นทางของ route ตัวอย่างเช่น การสร้างไฟล์ชื่อ /pages/api/hello.js จะสร้าง API endpoint ที่สามารถเข้าถึงได้ที่ /api/hello

ตัวอย่าง: API ทักทายอย่างง่าย

นี่คือตัวอย่างพื้นฐานของ API route ที่คืนค่าการตอบสนองแบบ JSON:


// pages/api/hello.js

export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js API Route!' });
}

โค้ดนี้กำหนดฟังก์ชัน handler แบบ asynchronous ที่รับอาร์กิวเมนต์สองตัว:

ฟังก์ชันนี้จะตั้งค่าโค้ดสถานะ HTTP เป็น 200 (OK) และคืนค่าการตอบสนองแบบ JSON พร้อมข้อความ

การจัดการ HTTP Methods ที่แตกต่างกัน

คุณสามารถจัดการ HTTP methods ที่แตกต่างกัน (GET, POST, PUT, DELETE, ฯลฯ) ภายใน API route ของคุณได้โดยการตรวจสอบ property req.method ซึ่งช่วยให้คุณสร้าง RESTful APIs ได้อย่างง่ายดาย


// pages/api/todos.js

export default async function handler(req, res) {
  if (req.method === 'GET') {
    // ดึงข้อมูล todos ทั้งหมดจากฐานข้อมูล
    const todos = await fetchTodos();
    res.status(200).json(todos);
  } else if (req.method === 'POST') {
    // สร้าง todo ใหม่
    const newTodo = await createTodo(req.body);
    res.status(201).json(newTodo);
  } else {
    // จัดการกับ methods ที่ไม่รองรับ
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

ตัวอย่างนี้สาธิตวิธีการจัดการคำขอ GET และ POST สำหรับ endpoint /api/todos สมมติ นอกจากนี้ยังมีการจัดการข้อผิดพลาดสำหรับ methods ที่ไม่รองรับอีกด้วย

รูปแบบการพัฒนา Full-Stack ด้วย API Routes

Next.js API Routes ช่วยให้สามารถใช้รูปแบบการพัฒนา full-stack ได้หลากหลาย นี่คือกรณีการใช้งานทั่วไปบางส่วน:

1. การดึงและจัดการข้อมูล

API Routes สามารถใช้เพื่อดึงข้อมูลจากฐานข้อมูล, API ภายนอก หรือแหล่งข้อมูลอื่นๆ นอกจากนี้ยังสามารถใช้เพื่อจัดการข้อมูล เช่น การสร้าง, อัปเดต หรือลบระเบียน

ตัวอย่าง: การดึงข้อมูลผู้ใช้จากฐานข้อมูล


// pages/api/users/[id].js
import { query } from '../../../lib/db';

export default async function handler(req, res) {
  const { id } = req.query;

  try {
    const results = await query(
      'SELECT * FROM users WHERE id = ?',
      [id]
    );

    if (results.length === 0) {
      return res.status(404).json({ message: 'User not found' });
    }

    res.status(200).json(results[0]);
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
}

ตัวอย่างนี้ดึงข้อมูลผู้ใช้จากฐานข้อมูลตาม ID ของผู้ใช้ที่ระบุใน URL โดยใช้ไลบรารีการสืบค้นฐานข้อมูล (สมมติว่าอยู่ใน lib/db) เพื่อโต้ตอบกับฐานข้อมูล สังเกตการใช้ parameterized queries เพื่อป้องกันช่องโหว่ SQL injection

2. การพิสูจน์ตัวตนและการให้สิทธิ์

API Routes สามารถใช้เพื่อใช้ตรรกะการพิสูจน์ตัวตน (authentication) และการให้สิทธิ์ (authorization) คุณสามารถใช้เพื่อตรวจสอบข้อมูลประจำตัวของผู้ใช้, สร้าง JWT tokens และปกป้องทรัพยากรที่ละเอียดอ่อนได้

ตัวอย่าง: การพิสูจน์ตัวตนผู้ใช้


// pages/api/login.js
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { query } from '../../lib/db';

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { email, password } = req.body;

    try {
      const results = await query(
        'SELECT * FROM users WHERE email = ?',
        [email]
      );

      if (results.length === 0) {
        return res.status(401).json({ message: 'Invalid credentials' });
      }

      const user = results[0];

      const passwordMatch = await bcrypt.compare(password, user.password);

      if (!passwordMatch) {
        return res.status(401).json({ message: 'Invalid credentials' });
      }

      const token = jwt.sign(
        { userId: user.id, email: user.email },
        process.env.JWT_SECRET,
        { expiresIn: '1h' }
      );

      res.status(200).json({ token });
    } catch (error) {
      console.error(error);
      res.status(500).json({ message: 'Internal Server Error' });
    }
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

ตัวอย่างนี้พิสูจน์ตัวตนผู้ใช้โดยการเปรียบเทียบรหัสผ่านที่ให้มากับรหัสผ่านที่ถูกแฮชและจัดเก็บไว้ในฐานข้อมูล หากข้อมูลประจำตัวถูกต้อง จะสร้าง JWT token และส่งคืนไปยังไคลเอนต์ จากนั้นไคลเอนต์สามารถใช้ token นี้เพื่อพิสูจน์ตัวตนในคำขอครั้งต่อไป

3. การจัดการฟอร์มและการส่งข้อมูล

API Routes สามารถใช้เพื่อจัดการการส่งฟอร์มและประมวลผลข้อมูลที่ส่งมาจากไคลเอนต์ ซึ่งมีประโยชน์สำหรับการสร้างฟอร์มติดต่อ, ฟอร์มลงทะเบียน และองค์ประกอบเชิงโต้ตอบอื่นๆ

ตัวอย่าง: การส่งฟอร์มติดต่อ


// pages/api/contact.js
import { sendEmail } from '../../lib/email';

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { name, email, message } = req.body;

    try {
      await sendEmail({
        to: 'admin@example.com',
        subject: 'New Contact Form Submission',
        text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`,
      });

      res.status(200).json({ message: 'Email sent successfully' });
    } catch (error) {
      console.error(error);
      res.status(500).json({ message: 'Failed to send email' });
    }
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

ตัวอย่างนี้จัดการการส่งฟอร์มติดต่อโดยการส่งอีเมลไปยังผู้ดูแลระบบ โดยใช้ไลบรารีการส่งอีเมล (สมมติว่าอยู่ใน lib/email) เพื่อส่งอีเมล คุณควรแทนที่ admin@example.com ด้วยที่อยู่อีเมลของผู้รับจริง

4. Webhooks และการจัดการเหตุการณ์

API Routes สามารถใช้เพื่อจัดการ webhooks และตอบสนองต่อเหตุการณ์จากบริการภายนอก ซึ่งช่วยให้คุณสามารถรวมแอปพลิเคชัน Next.js ของคุณเข้ากับแพลตฟอร์มอื่น ๆ และทำงานอัตโนมัติได้

ตัวอย่าง: การจัดการ Stripe Webhook


// pages/api/stripe-webhook.js
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export const config = {
  api: {
    bodyParser: false, // ปิดการใช้งาน body parsing เริ่มต้น
  },
};

async function buffer(req) {
  const chunks = [];
  for await (const chunk of req) {
    chunks.push(chunk);
  }
  return Buffer.concat(chunks).toString();
}

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const sig = req.headers['stripe-signature'];

    let event;

    try {
      const buf = await buffer(req);
      event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK_SECRET);
    } catch (err) {
      console.log(`Webhook Error: ${err.message}`);
      res.status(400).send(`Webhook Error: ${err.message}`);
      return;
    }

    // จัดการกับเหตุการณ์
    switch (event.type) {
      case 'payment_intent.succeeded':
        const paymentIntent = event.data.object;
        console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
        // จากนั้นกำหนดและเรียกใช้เมธอดเพื่อจัดการกับ payment intent ที่สำเร็จ
        // handlePaymentIntentSucceeded(paymentIntent);
        break;
      case 'payment_method.attached':
        const paymentMethod = event.data.object;
        // จากนั้นกำหนดและเรียกใช้เมธอดเพื่อจัดการกับการแนบ PaymentMethod ที่สำเร็จ
        // handlePaymentMethodAttached(paymentMethod);
        break;
      default:
        // ประเภทของ event ที่ไม่คาดคิด
        console.log(`Unhandled event type ${event.type}.`);
    }

    // คืนค่าการตอบสนอง 200 เพื่อยืนยันการรับเหตุการณ์
    res.status(200).json({ received: true });
  } else {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
  }
}

ตัวอย่างนี้จัดการ Stripe webhook โดยการตรวจสอบลายเซ็นและประมวลผลข้อมูลเหตุการณ์ โดยปิดการใช้งาน body parser เริ่มต้นและใช้ฟังก์ชัน buffer ที่กำหนดเองเพื่ออ่าน request body ดิบ การปิดใช้งาน body parser เริ่มต้นเป็นสิ่งสำคัญเนื่องจาก Stripe ต้องการ body ดิบสำหรับการตรวจสอบลายเซ็น อย่าลืมกำหนดค่า Stripe webhook endpoint ของคุณในแดชบอร์ด Stripe และตั้งค่าตัวแปรสภาพแวดล้อม STRIPE_WEBHOOK_SECRET

แนวทางปฏิบัติที่ดีที่สุดสำหรับ API Routes

เพื่อให้มั่นใจในคุณภาพและความสามารถในการบำรุงรักษา API Routes ของคุณ ให้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้:

1. แบ่งโค้ดของคุณเป็นโมดูล

หลีกเลี่ยงการเขียน API routes ขนาดใหญ่และซับซ้อน ควรแบ่งโค้ดของคุณออกเป็นโมดูลขนาดเล็กที่สามารถนำกลับมาใช้ใหม่ได้ ซึ่งทำให้โค้ดของคุณเข้าใจ, ทดสอบ และบำรุงรักษาง่ายขึ้น

2. ใช้การจัดการข้อผิดพลาด

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

3. ตรวจสอบข้อมูลอินพุต

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

4. ปกป้องข้อมูลที่ละเอียดอ่อน

จัดเก็บข้อมูลที่ละเอียดอ่อน เช่น API keys และข้อมูลรับรองฐานข้อมูล ในตัวแปรสภาพแวดล้อม อย่า commit ข้อมูลที่ละเอียดอ่อนไปยัง code repository ของคุณ

5. ใช้การจำกัดอัตรา (Rate Limiting)

ปกป้อง API routes ของคุณจากการใช้งานในทางที่ผิดโดยการใช้ rate limiting ซึ่งจะจำกัดจำนวนคำขอที่ไคลเอนต์สามารถทำได้ภายในช่วงเวลาที่กำหนด ใช้ไลบรารี rate limiting เช่น express-rate-limit หรือ limiter

6. รักษาความปลอดภัยของ API Keys

อย่าเปิดเผย API keys โดยตรงในโค้ดฝั่งไคลเอนต์ ให้ส่งคำขอผ่าน API routes ของคุณเสมอเพื่อปกป้อง API keys ของคุณจากการเข้าถึงโดยไม่ได้รับอนุญาต จัดเก็บ API keys อย่างปลอดภัยในตัวแปรสภาพแวดล้อมบนเซิร์ฟเวอร์ของคุณ

7. ใช้ตัวแปรสภาพแวดล้อม (Environment Variables)

หลีกเลี่ยงการ hardcode ค่าการกำหนดค่าในโค้ดของคุณ ให้ใช้ตัวแปรสภาพแวดล้อมเพื่อจัดเก็บการตั้งค่าการกำหนดค่าแทน ซึ่งทำให้การจัดการแอปพลิเคชันของคุณในสภาพแวดล้อมต่างๆ (development, staging, production) ง่ายขึ้น

8. การบันทึกและการติดตาม (Logging and Monitoring)

ใช้การบันทึกและการติดตามเพื่อติดตามประสิทธิภาพของ API routes ของคุณ บันทึกเหตุการณ์สำคัญ เช่น ข้อผิดพลาด, คำเตือน และคำขอที่สำเร็จ ใช้เครื่องมือติดตามเพื่อติดตามเมตริก เช่น เวลาแฝงของคำขอ, อัตราข้อผิดพลาด และการใช้ทรัพยากร บริการต่างๆ เช่น Sentry, Datadog หรือ New Relic สามารถช่วยได้

ข้อควรพิจารณาในการ Deployment

Next.js API Routes ถูกออกแบบมาเพื่อ deploy บนแพลตฟอร์ม serverless ตัวเลือกการ deploy ยอดนิยม ได้แก่:

เมื่อ deploy แอปพลิเคชัน Next.js ของคุณที่มี API Routes ตรวจสอบให้แน่ใจว่าตัวแปรสภาพแวดล้อมของคุณได้รับการกำหนดค่าอย่างถูกต้องบนแพลตฟอร์มการ deploy นอกจากนี้ ให้พิจารณาเวลา cold start ของ serverless functions ซึ่งอาจส่งผลต่อเวลาตอบสนองเริ่มต้นของ API routes ของคุณ การปรับปรุงโค้ดของคุณและใช้เทคนิคต่างๆ เช่น provisioned concurrency สามารถช่วยลดปัญหา cold start ได้

สรุป

Next.js API Routes เป็นวิธีที่ทรงพลังและสะดวกในการสร้างแอปพลิเคชัน full-stack ด้วย React ด้วยการใช้ประโยชน์จาก serverless functions คุณสามารถทำให้การพัฒนาง่ายขึ้น, ลดภาระในการดำเนินงาน และปรับปรุงประสิทธิภาพของแอปพลิเคชันได้ การปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดที่ระบุไว้ในบทความนี้จะช่วยให้คุณสร้าง API Routes ที่แข็งแกร่งและบำรุงรักษาได้ ซึ่งเป็นขุมพลังให้กับแอปพลิเคชัน Next.js ของคุณ

ไม่ว่าคุณจะสร้างฟอร์มติดต่อง่ายๆ หรือแพลตฟอร์มอีคอมเมิร์ซที่ซับซ้อน Next.js API Routes สามารถช่วยให้กระบวนการพัฒนาของคุณคล่องตัวขึ้นและมอบประสบการณ์ผู้ใช้ที่ยอดเยี่ยม

เรียนรู้เพิ่มเติม