ข้ามไปยังเนื้อหา

บล็อก

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

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

  • อ่าน 9 นาที
  • Updated

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 ที่แข็งแกร่งต้องอาศัยความใส่ใจในสถาปัตยกรรม ความปลอดภัย ประสิทธิภาพ และข้อกังวลด้านการปฏิบัติงาน ด้วยการสร้างโครงสร้างโปรเจ็กต์ใหม่ทั้งหมด การใช้การรับรองความถูกต้องและการตรวจสอบที่เหมาะสม การเพิ่มประสิทธิภาพการสืบค้นฐานข้อมูล และการจัดคอนเทนเนอร์การใช้งานของคุณ คุณจะสร้างแบ็กเอนด์ที่ปลอดภัย มีประสิทธิภาพ และบำรุงรักษาได้ เริ่มต้นด้วยพื้นฐานที่ระบุไว้ในคู่มือนี้ วัดประสิทธิภาพของแอปพลิเคชันของคุณภายใต้ภาระงานจริง และย้ำจุดคอขวดที่คุณค้นพบ รูปแบบที่นำเสนอในที่นี้ได้รับการพิสูจน์แล้วในแอปพลิเคชันที่ใช้งานจริงหลายพันรายการ และจะทำหน้าที่เป็นรากฐานที่มั่นคงสำหรับระบบแบ็กเอนด์ของคุณ