Workstation Logo
ผลิตภัณฑ์
AI LabsOpenAI AgentsClaude AgentsGrok BotWorkstation CRM (WSL CRM)การตลาดผลิตภัณฑ์ทั้งหมด
โซลูชัน AI
เวิร์กสเตชัน AIAI SME PackagesAI ส่วนตัวคลัสเตอร์ GPUEdge AIแล็บ AI องค์กรAI ตามอุตสาหกรรม
บริการ
Platform ModernisationDigital EngineeringData Foundations & AIAutonomous Operationsที่ปรึกษา AIระบบอัตโนมัติ DevOpsความมั่นคงปลอดภัยไซเบอร์การพัฒนาซอฟต์แวร์การสร้างเอเจนต์การตั้งค่า MLOps
เกี่ยวกับเรา
พาร์ทเนอร์เรื่องราวลูกค้า
บทความ
เอกสาร
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
บล็อก
ติดต่อเราLogin
Workstation

เวิร์กสเตชัน AI ซอฟต์แวร์มัลติเอเจนต์ AI โครงสร้างพื้นฐาน GPU และโซลูชันเอเจนต์อัจฉริยะสำหรับธุรกิจยุคใหม่

ติดต่อเรา

โซลูชัน AI

เวิร์กสเตชัน AIAI SME PackagesAI ส่วนตัวคลัสเตอร์ GPUEdge AIแล็บ AI องค์กรAI ตามอุตสาหกรรม

ผลิตภัณฑ์

ผลิตภัณฑ์ทั้งหมดWSL CRM และ ERPการตลาดOpenAI AgentsWSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

บริษัท

เกี่ยวกับเราทำไมต้อง Workstationพาร์ทเนอร์เรื่องราวลูกค้าราคาติดต่อ

แหล่งข้อมูล

บทความเอกสารประกอบบล็อกค้นหาแผนผังเว็บไซต์
สำนักงานสหราชอาณาจักร
77-79 Marlowes, Hemel Hempstead HP1 1LFเส้นทาง - ออกทางแยกที่ 20 จาก M25 Outer Londonเลขทะเบียนบริษัท: 11641870จ. - ศ.: 9:00 - 18:00 น. GMT
+44 7515 356 146
สำนักงานเบลเยียม
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683จ. - ศ.: 9:00 - 18:00 น. CET
+32 492 45 67 46
สำนักงานอินเดีย
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI สงวนลิขสิทธิ์

ความเป็นส่วนตัวคุกกี้ข้อกำหนดการให้บริการแผนผังเว็บไซต์

Loading blog...

Home / Blog
WebFrontendReact

การสร้างระบบแบ็กเอนด์ที่แข็งแกร่งด้วย Node.js และ MySQL

ออกแบบและใช้งาน API แบ็กเอนด์ที่ปลอดภัยและปรับขนาดได้ด้วย Node.js, Express และ MySQL

Balinder Walia27 พฤษภาคม 25689 min read

Node.js และ MySQL ยังคงเป็นหนึ่งในการผสมผสานที่เชื่อถือได้และผ่านการทดสอบการต่อสู้แล้วสำหรับการสร้างระบบแบ็กเอนด์ Node.js มอบโมเดล I/O ที่ขับเคลื่อนด้วยเหตุการณ์และไม่มีการบล็อก ซึ่งจัดการคำขอที่เกิดขึ้นพร้อมกันได้อย่างมีประสิทธิภาพ ในขณะที่ MySQL มอบความสมบูรณ์ของข้อมูลเชิงสัมพันธ์ที่แอปพลิเคชันทางธุรกิจต้องการ พวกเขาร่วมกันสร้างรากฐานที่ขับเคลื่อนทุกสิ่งตั้งแต่ MVP สตาร์ทอัพไปจนถึงแพลตฟอร์มระดับองค์กรที่จัดการคำขอหลายล้านคำขอต่อวัน

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

Express.js โครงสร้างโครงการ

Node.js + สถาปัตยกรรมแบ็กเอนด์ด่วนเลเยอร์มิดเดิลแวร์Auth (JWT)การตรวจสอบขีดจำกัดอัตราCORSHelmetการบันทึกไคลเอ็นต์เบราว์เซอร์ / แอปAPIGateway/api/v1Expressเส้นทางรับ/โพสต์/ใส่/DELตัวควบคุมการจัดการคำขอบริการตรรกะทางธุรกิจMySQLRelational DBHTTPเส้นทางโทรโทรแบบสอบถามรูปแบบการตอบสนอง{ ความสำเร็จ: จริง,ข้อมูล: [...],การแบ่งหน้า: {...}}ข้อผิดพลาดในการจัดการไปป์ไลน์ApiErrorasyncHandlererrorHandlerการตอบสนองข้อผิดพลาดแบบรวมศูนย์และสม่ำเสมอ

โครงสร้างโปรเจ็กต์ที่มีการจัดระเบียบอย่างดีเป็นรากฐานของแบ็กเอนด์ที่บำรุงรักษาได้ แยกข้อกังวลอย่างชัดเจนและกำหนดแบบแผนแต่เนิ่นๆ

project-root/
  src/
    config/
      database.js
      environment.js
      logger.js
    middleware/
      auth.js
      errorHandler.js
      rateLimiter.js
      validator.js
    models/
      User.js
      Product.js
      Order.js
      index.js
    routes/
      auth.routes.js
      users.routes.js
      products.routes.js
      orders.routes.js
      index.js
    services/
      auth.service.js
      user.service.js
      product.service.js
      email.service.js
    utils/
      ApiError.js
      asyncHandler.js
      pagination.js
    app.js
    server.js
  migrations/
  seeders/
  tests/
  .env
  .env.example
  package.json

จุดเริ่มต้นตั้งค่า Express ด้วยมิดเดิลแวร์ที่จำเป็น:

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const { errorHandler } = require('./middleware/errorHandler');
const routes = require('./routes');

const app = express();

// Security middleware
app.use(helmet());
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
  credentials: true,
}));

// Request parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// Logging
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// API routes
app.use('/api/v1', routes);

// Error handling (must be last)
app.use(errorHandler);

module.exports = app;

RESTful API Design

ออกแบบจุดสิ้นสุด API ของคุณตามแบบแผน REST ใช้คำนามสำหรับทรัพยากร วิธี HTTP สำหรับการกระทำ และรูปแบบการตอบสนองที่สอดคล้องกัน

// src/routes/products.routes.js
const router = require('express').Router();
const { authenticate, authorize } = require('../middleware/auth');
const { validate } = require('../middleware/validator');
const { createProductSchema, updateProductSchema } = require('../validators/product');
const productController = require('../controllers/product.controller');

router.get('/', productController.getAll);
router.get('/:id', productController.getById);
router.post('/',
  authenticate,
  authorize('admin'),
  validate(createProductSchema),
  productController.create
);
router.put('/:id',
  authenticate,
  authorize('admin'),
  validate(updateProductSchema),
  productController.update
);
router.delete('/:id',
  authenticate,
  authorize('admin'),
  productController.delete
);

module.exports = router;

คอนโทรลเลอร์ควรบาง โดยมอบหมายตรรกะทางธุรกิจให้กับคลาสบริการ:

// src/controllers/product.controller.js
const productService = require('../services/product.service');
const { asyncHandler } = require('../utils/asyncHandler');

exports.getAll = asyncHandler(async (req, res) => {
  const { page = 1, limit = 20, sort = 'created_at', order = 'DESC', search } = req.query;

  const result = await productService.findAll({
    page: parseInt(page),
    limit: Math.min(parseInt(limit), 100),
    sort,
    order,
    search,
  });

  res.json({
    success: true,
    data: result.products,
    pagination: {
      page: result.page,
      limit: result.limit,
      total: result.total,
      totalPages: result.totalPages,
    },
  });
});

exports.create = asyncHandler(async (req, res) => {
  const product = await productService.create(req.body);
  res.status(201).json({
    success: true,
    data: product,
  });
});

MySQL Connection Pooling with mysql2

MySQL สถาปัตยกรรมพูลการเชื่อมต่อแอปพลิเคชันคำขอพร้อมกันเธรดคำขอ 1เธรดคำขอ 2เธรดคำขอ 3ขอเธรด 4...เธรดคำขอ Nพูลการเชื่อมต่อ (mysql2)Activeไม่ได้ใช้งานConnection 1 — Activeการเชื่อมต่อ2 — ใช้งานอยู่การเชื่อมต่อ 3 — ใช้งานอยู่การเชื่อมต่อ4 — ไม่ได้ใช้งานการเชื่อมต่อ 5 — ไม่ทำงานการเชื่อมต่อจำกัด: 10 | waitForConnections: จริงเปิดใช้งาน KeepAlive: จริง | QueueLimit: 0MySQLเซิร์ฟเวอร์ฐานข้อมูลที่เก็บข้อมูลดัชนีInnoDB EnginePool นำการเชื่อมต่อกลับมาใช้ใหม่ โดยหลีกเลี่ยงค่าใช้จ่ายของ การสร้างใหม่ตามคำขอ

การรวมการเชื่อมต่อมีความสำคัญต่อประสิทธิภาพการทำงาน แพ็คเกจmysql2มอบ API ตามสัญญาพร้อมคำสั่งที่เตรียมไว้และการรวมการเชื่อมต่อทันที

// src/config/database.js
const mysql = require('mysql2/promise');
const logger = require('./logger');

const pool = mysql.createPool({
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT) || 3306,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: parseInt(process.env.DB_POOL_SIZE) || 10,
  queueLimit: 0,
  enableKeepAlive: true,
  keepAliveInitialDelay: 30000,
  timezone: '+00:00',
  typeCast: function (field, next) {
    if (field.type === 'TINY' && field.length === 1) {
      return field.string() === '1';
    }
    return next();
  },
});

// Test connection on startup
pool.getConnection()
  .then(conn => {
    logger.info('MySQL connected successfully');
    conn.release();
  })
  .catch(err => {
    logger.error('MySQL connection failed:', err.message);
    process.exit(1);
  });

module.exports = pool;

ใช้การสืบค้นแบบกำหนดพารามิเตอร์เสมอเพื่อป้องกันการแทรก SQL:

// NEVER do this
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ALWAYS use parameterized queries
const [rows] = await pool.execute(
  'SELECT id, email, first_name, last_name FROM users WHERE email = ?',
  [email]
);

Sequelize ORM

สำหรับแอปพลิเคชันที่มีความสัมพันธ์ของข้อมูลที่ซับซ้อน Sequelize จัดเตรียม ORM ที่มีคุณลักษณะครบถ้วนพร้อมคำจำกัดความของโมเดล การเชื่อมโยง การโยกย้าย และการสร้างคิวรี

// src/models/Product.js
const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize');

const Product = sequelize.define('Product', {
  id: {
    type: DataTypes.UUID,
    defaultValue: DataTypes.UUIDV4,
    primaryKey: true,
  },
  name: {
    type: DataTypes.STRING(255),
    allowNull: false,
    validate: {
      notEmpty: true,
      len: [2, 255],
    },
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
  price: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false,
    validate: {
      min: 0,
    },
  },
  sku: {
    type: DataTypes.STRING(100),
    unique: true,
    allowNull: false,
  },
  stock_quantity: {
    type: DataTypes.INTEGER,
    defaultValue: 0,
    validate: {
      min: 0,
    },
  },
  is_active: {
    type: DataTypes.BOOLEAN,
    defaultValue: true,
  },
}, {
  tableName: 'products',
  timestamps: true,
  underscored: true,
  paranoid: true, // Soft deletes
  indexes: [
    { fields: ['sku'], unique: true },
    { fields: ['is_active'] },
    { fields: ['price'] },
    { fields: ['created_at'] },
  ],
});

// Associations
Product.associate = (models) => {
  Product.belongsTo(models.Category, { foreignKey: 'category_id' });
  Product.hasMany(models.OrderItem, { foreignKey: 'product_id' });
  Product.belongsToMany(models.Tag, { through: 'product_tags' });
};

module.exports = Product;

การตรวจสอบสิทธิ์ด้วย JWT

ใช้การตรวจสอบสิทธิ์แบบไร้สถานะโดยใช้ JSON Web Tokens ใช้โทเค็นการเข้าถึงสำหรับคำขอ API และโทเค็นการรีเฟรชสำหรับการจัดการเซสชัน

// src/services/auth.service.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { User } = require('../models');
const ApiError = require('../utils/ApiError');

const SALT_ROUNDS = 12;
const ACCESS_TOKEN_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';

exports.register = async ({ email, password, firstName, lastName }) => {
  const existingUser = await User.findOne({ where: { email } });
  if (existingUser) {
    throw new ApiError(409, 'Email already registered');
  }

  const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
  const user = await User.create({
    email,
    password: hashedPassword,
    first_name: firstName,
    last_name: lastName,
  });

  const tokens = generateTokens(user);
  return { user: sanitizeUser(user), ...tokens };
};

exports.login = async ({ email, password }) => {
  const user = await User.findOne({ where: { email } });
  if (!user || !(await bcrypt.compare(password, user.password))) {
    throw new ApiError(401, 'Invalid email or password');
  }

  const tokens = generateTokens(user);
  return { user: sanitizeUser(user), ...tokens };
};

function generateTokens(user) {
  const accessToken = jwt.sign(
    { userId: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: ACCESS_TOKEN_EXPIRY }
  );

  const refreshToken = jwt.sign(
    { userId: user.id, tokenType: 'refresh' },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: REFRESH_TOKEN_EXPIRY }
  );

  return { accessToken, refreshToken };
}

function sanitizeUser(user) {
  const { password, ...userData } = user.toJSON();
  return userData;
}

มิดเดิลแวร์การตรวจสอบความถูกต้องจะตรวจสอบโทเค็นบนเส้นทางที่ได้รับการป้องกัน:

// src/middleware/auth.js
const jwt = require('jsonwebtoken');
const ApiError = require('../utils/ApiError');

exports.authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    throw new ApiError(401, 'Access token required');
  }

  const token = authHeader.split(' ')[1];
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      throw new ApiError(401, 'Access token expired');
    }
    throw new ApiError(401, 'Invalid access token');
  }
};

exports.authorize = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      throw new ApiError(403, 'Insufficient permissions');
    }
    next();
  };
};

การตรวจสอบอินพุตด้วย Joi

ตรวจสอบข้อมูลที่เข้ามาทั้งหมดก่อนที่จะถึงตรรกะทางธุรกิจของคุณ Joi จัดเตรียมไลบรารีการตรวจสอบตามสคีมาที่มีประสิทธิภาพ

// src/validators/product.js
const Joi = require('joi');

exports.createProductSchema = Joi.object({
  name: Joi.string().min(2).max(255).required(),
  description: Joi.string().max(5000).optional(),
  price: Joi.number().positive().precision(2).required(),
  sku: Joi.string().alphanum().max(100).required(),
  stock_quantity: Joi.number().integer().min(0).default(0),
  category_id: Joi.string().uuid().required(),
  tags: Joi.array().items(Joi.string().uuid()).optional(),
  is_active: Joi.boolean().default(true),
});

exports.updateProductSchema = Joi.object({
  name: Joi.string().min(2).max(255),
  description: Joi.string().max(5000).allow(null),
  price: Joi.number().positive().precision(2),
  stock_quantity: Joi.number().integer().min(0),
  category_id: Joi.string().uuid(),
  is_active: Joi.boolean(),
}).min(1);

// src/middleware/validator.js
exports.validate = (schema) => {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, {
      abortEarly: false,
      stripUnknown: true,
    });

    if (error) {
      const errors = error.details.map(detail => ({
        field: detail.path.join('.'),
        message: detail.message,
      }));
      return res.status(400).json({
        success: false,
        message: 'Validation failed',
        errors,
      });
    }

    req.body = value;
    next();
  };
};

การจัดการข้อผิดพลาดมิดเดิลแวร์

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

// src/utils/ApiError.js
class ApiError extends Error {
  constructor(statusCode, message, errors = []) {
    super(message);
    this.statusCode = statusCode;
    this.errors = errors;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = ApiError;

// src/utils/asyncHandler.js
exports.asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// src/middleware/errorHandler.js
const logger = require('../config/logger');

exports.errorHandler = (err, req, res, next) => {
  let statusCode = err.statusCode || 500;
  let message = err.message || 'Internal Server Error';

  // Sequelize validation errors
  if (err.name === 'SequelizeValidationError') {
    statusCode = 400;
    message = 'Validation error';
  }

  // Sequelize unique constraint
  if (err.name === 'SequelizeUniqueConstraintError') {
    statusCode = 409;
    message = 'Resource already exists';
  }

  // Log server errors
  if (statusCode >= 500) {
    logger.error({
      message: err.message,
      stack: err.stack,
      url: req.originalUrl,
      method: req.method,
      ip: req.ip,
    });
  }

  res.status(statusCode).json({
    success: false,
    message,
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
    ...(err.errors?.length && { errors: err.errors }),
  });
};

การเพิ่มประสิทธิภาพแบบสอบถามและการจัดทำดัชนี

การสืบค้นฐานข้อมูลที่มีประสิทธิภาพมีความสำคัญอย่างยิ่งต่อประสิทธิภาพของแบ็กเอนด์ ปฏิบัติตามกลยุทธ์เหล่านี้เพื่อให้การสืบค้น MySQL ของคุณรวดเร็ว

  • ใช้ดัชนีในคอลัมน์ที่มีการสืบค้นบ่อย- เพิ่มดัชนีในคอลัมน์ที่ใช้ในคำสั่ง WHERE เงื่อนไข JOIN และคำสั่ง ORDER BY ดัชนีคอมโพสิตควรเป็นไปตามกฎคำนำหน้าซ้ายสุด
  • หลีกเลี่ยง SELECT *- ระบุคอลัมน์ที่คุณต้องการเสมอ ซึ่งจะช่วยลดการถ่ายโอนข้อมูลและอนุญาตให้ MySQL ใช้ดัชนีที่ครอบคลุม
  • ใช้ EXPLAIN เพื่อวิเคราะห์การสืบค้น- เรียกใช้EXPLAINก่อนการสืบค้นของคุณเพื่อทำความเข้าใจแผนการดำเนินการ ค้นหาการสแกนตารางแบบเต็ม การดำเนินการจัดเรียงไฟล์ และตารางชั่วคราว
  • ปรับการแบ่งหน้าให้เหมาะสม- สำหรับชุดข้อมูลขนาดใหญ่ ให้ใช้การแบ่งหน้าตามเคอร์เซอร์ (การแบ่งหน้าชุดคีย์) แทน OFFSET ซึ่งจะช้าเมื่อมีหมายเลขหน้าสูง
// Inefficient OFFSET pagination
const [rows] = await pool.execute(
  'SELECT * FROM products ORDER BY created_at DESC LIMIT ? OFFSET ?',
  [limit, (page - 1) * limit]
);

// Efficient cursor-based pagination
const [rows] = await pool.execute(
  `SELECT id, name, price, created_at FROM products
   WHERE created_at < ?
   ORDER BY created_at DESC
   LIMIT ?`,
  [cursor, limit]
);

การย้ายฐานข้อมูล

ห้ามแก้ไขฐานข้อมูลการผลิตด้วยตนเอง ใช้ Sequelize Migration สำหรับการเปลี่ยนแปลงสคีมาที่ควบคุมเวอร์ชัน

// migrations/20250101000000-create-products-table.js
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable('products', {
      id: {
        type: Sequelize.UUID,
        defaultValue: Sequelize.UUIDV4,
        primaryKey: true,
      },
      name: {
        type: Sequelize.STRING(255),
        allowNull: false,
      },
      price: {
        type: Sequelize.DECIMAL(10, 2),
        allowNull: false,
      },
      sku: {
        type: Sequelize.STRING(100),
        unique: true,
        allowNull: false,
      },
      category_id: {
        type: Sequelize.UUID,
        references: {
          model: 'categories',
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      created_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
      },
      updated_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'),
      },
    });

    await queryInterface.addIndex('products', ['sku']);
    await queryInterface.addIndex('products', ['category_id']);
    await queryInterface.addIndex('products', ['created_at']);
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable('products');
  },
};

การจำกัดอัตรา

ปกป้อง API ของคุณจากการละเมิดด้วยการจำกัดอัตรา ใช้express-rate-limitกับร้านค้า Redis สำหรับการปรับใช้แบบกระจาย

// src/middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('../config/redis');

exports.apiLimiter = rateLimit({
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,
  message: {
    success: false,
    message: 'Too many requests, please try again later',
  },
  standardHeaders: true,
  legacyHeaders: false,
});

exports.authLimiter = rateLimit({
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
  windowMs: 15 * 60 * 1000,
  max: 5,
  message: {
    success: false,
    message: 'Too many login attempts, please try again later',
  },
  skipSuccessfulRequests: true,
});

การบันทึกด้วย Winston

แอปพลิเคชันการผลิตจำเป็นต้องมีการบันทึกแบบมีโครงสร้างที่มีการเคลื่อนย้ายและระดับการบันทึกหลายระดับ

// src/config/logger.js
const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'api-server' },
  transports: [
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 5242880, // 5MB
      maxFiles: 5,
    }),
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 5242880,
      maxFiles: 10,
    }),
  ],
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    ),
  }));
}

module.exports = logger;

Docker Deployment

จัดคอนเทนเนอร์แอปพลิเคชันของคุณเพื่อการปรับใช้ที่สอดคล้องกันในทุกสภาพแวดล้อม

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup
COPY --from=builder /app/node_modules ./node_modules
COPY src/ ./src/
COPY migrations/ ./migrations/
COPY package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "src/server.js"]
# docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=mysql
      - DB_USER=app_user
      - DB_PASSWORD_FILE=/run/secrets/db_password
      - DB_NAME=myapp
    depends_on:
      mysql:
        condition: service_healthy
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
      MYSQL_DATABASE: myapp
      MYSQL_USER: app_user
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mysql_data:

สรุป

การสร้างแบ็กเอนด์ Node.js และ MySQL ที่แข็งแกร่งต้องอาศัยความใส่ใจในสถาปัตยกรรม ความปลอดภัย ประสิทธิภาพ และข้อกังวลด้านการปฏิบัติงาน ด้วยการสร้างโครงสร้างโปรเจ็กต์ใหม่ทั้งหมด การใช้การรับรองความถูกต้องและการตรวจสอบที่เหมาะสม การเพิ่มประสิทธิภาพการสืบค้นฐานข้อมูล และการจัดคอนเทนเนอร์การใช้งานของคุณ คุณจะสร้างแบ็กเอนด์ที่ปลอดภัย มีประสิทธิภาพ และบำรุงรักษาได้ เริ่มต้นด้วยพื้นฐานที่ระบุไว้ในคู่มือนี้ วัดประสิทธิภาพของแอปพลิเคชันของคุณภายใต้ภาระงานจริง และย้ำจุดคอขวดที่คุณค้นพบ รูปแบบที่นำเสนอในที่นี้ได้รับการพิสูจน์แล้วในแอปพลิเคชันที่ใช้งานจริงหลายพันรายการ และจะทำหน้าที่เป็นรากฐานที่มั่นคงสำหรับระบบแบ็กเอนด์ของคุณ