created at 2025-11-12 09:58,updated at 2025-11-29 06:09

🚀 Framework Comparison

Framework Type Performance Learning Curve TypeScript Support Best Used For
Express.js Minimalist Good Low Partial General-purpose web apps, APIs
Nest.js Full-featured Good High Excellent Enterprise apps, complex APIs
Fastify Minimalist Excellent Medium Good High-performance APIs
Koa.js Minimalist Very Good Medium Good Modern, async-focused apps
Hapi.js Full-featured Good Medium Good Enterprise apps, configuration-driven
Adonis.js Full-stack MVC Good High Excellent Full-stack applications
Restify API-focused Good Low Partial RESTful APIs
Meteor Full-stack Moderate Medium Good Reactive full-stack apps
Loopback API-focused Good Medium Excellent API generation with minimal coding
Strapi Headless CMS Good Low (UI) Good Content management, API creation

🚀 API文档

https://expressjs.com/5x/api.html
https://expressjs.com/5x/api.html#req.app
https://expressjs.com/5x/api.html#res.app
https://expressjs.com/guide/writing-middleware.html

🚀 基本写法

https://www.w3schools.com/nodejs/nodejs_express.asp

事前准备:install

npm init -y // NodeJSプロジェクト初期化

npm install express
npm install typescript
npm install @types/node
npm install @types/express
或者:npm install express typescript @types/node @types/express

npx tsc --init // TypeScript初期化
tsconfig.json     // 入力出力設定
    "compilerOptions": {
    // File Layout
    "rootDir": "./src",
    "outDir": "./dist",

自動编译:
①npx tsc --watch    // 编译 .ts → .js
②VS Code のメニューから [ターミナル] -> [タスクの実行...] を選択、「tsc: watch - tsconfig.json」を入力

基本例

Tip

import express from 'express';
const app=express();
app.get('/', (req,res)=>{ } )
app.post('/', (req,res)=>{ } )
app.put('/:id', (req,res)=>{ } )
app.delete('/:id', (req,res)=>{ } )
app.use( (req,res,next)=>{ next(); }) // 中间件
app.listen( 3000, ()=>{ console.log('Server is running on 3000') })

import path from 'path';
const __dirname = import.meta.dirname;

import express from 'express';
const app = express();

// Middleware for parsing JSON
app.use(express.json());

const mid = function (req, res, next) {    // ミドルウェア定義
    console.log({ msg: `${new Date()} ${req.method} ${req.originalUrl}` })
    next();
}
app.use(mid);  // ミドルウェア

app.use(express.static(__dirname, 'public', { maxAge: 86400000 }));  // 静的ファイルのキャッシュ機能(1日間キャッシュ有効)

app.get('/image', (req, res) => {    // 静的ファイル
  res.sendFile(path.join(__dirname, 'public','images','test.png'));
});


let users = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' }
];

// GET - Retrieve all users
app.get('/api/users', (req, res) => { 
  if (!users) return res.status(404).json({ message: 'User not found' });
  res.json(users);
});

// GET - Retrieve a specific user
app.get('/api/users/:id', (req, res) => {  // 使用${req.query}获取值    /api/users/123
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).json({ message: 'User not found' });
  res.json(user);
});

app.get('/api/users/:id/book/:bookID', (req, res) => {    // 使用${req.params}获取值    /api/users/123/book/aaaa
    res.status(200).send({ ID: `${req.params.id}`, bookID: `${req.params.bookID}` });
})

app.get('/api/search', (req, res) => {  // 使用${req.query}获取值    /api/search?id=express&mail=2
    res.status(200).send({ id:`${req.query.id}`, mail: `${req.query.mail}` });
})

// POST - Create a new user
app.post('/api/users', (req, res) => {  // POST 和 GET 可以匹配同一路由
  const newUser = {
    id: users.length + 1,
    name: req.body.name,
    email: req.body.email
  };
  users.push(newUser);
  res.status(201).json(newUser);
});

// PUT - Update a user completely
app.put('/api/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).json({ message: 'User not found' });
  user.name = req.body.name;
  user.email = req.body.email;
  res.json(user);
});

// DELETE - Remove a user
app.delete('/api/users/:id', (req, res) => {
  const userIndex = users.findIndex(u => u.id === parseInt(req.params.id));
  if (userIndex === -1) return res.status(404).json({ message: 'User not found' });
  const deletedUser = users.splice(userIndex, 1);
  res.json(deletedUser[0]);
});


app.use((req, res) => {
    console.log(path.join(__dirname, '../public', '404.html'));
    res.status(404).sendFile(path.join(__dirname, '../public', '404.html'));
})

app.listen(3000, (err) => {
    if (err) console.log(err);
    console.log("Server is run on 3000");
})
curl -X GET http://localhost:3000/api/users
> {"msg":"GET OK"}

curl -X POST http://localhost:3000/api/users
> {"msg":"POST OK"}   

curl -X PUT http://localhost:3000/api/users/123
> {"id":"123","msg":"PUT OK"}

curl -X DELETE http://localhost:3000/api/users/123
> {"id":"123","msg":"DELETE OK"}

curl -X POST http://localhost:3000/api/users  -H 'Content-Type: application/json' -d '{"name":"太郎", "age":"30"}'  

🚀ExpressRequest

app.get('/', (req, res) => {

    req.host              // 主机名 'example.com:3000'
    req.hostname           //主机名 'example.com'
    req.ip                 // 客户端ip ::ffff:127.0.0.1
    req.method             // HTTP method of the request: GET, POST, PUT, and so on
    req.originalUrl    // /根路径后面的部分
    req.params        // 路由参数(例:/:id)
    req.query              // url 参数  ?后面的部分(例:?a=dadda&b=dasda)
    req.body               // 配合app.use(express.json())、获取·json

    req.headers            //获取全部头
    req.get('content-type')  // 获取content-type
    req.get('user-agent')   // 获取user-agent
})

🚀ExpressResponse

app.get('/', (req, res) => {

    res.status(403).end()
    res.status(400).send('Bad Request')
    res.status(404).sendFile('/absolute/path/to/404.png')
    res.status(200).json( {id:1,name:"Peter"}) 
    
    - res.status()   // 返回状态
    - res.send()    // 返回多种形式数据
    - res.end()    // 结束响应
    - res.json()    // 返回json
    
    res.set('Content-Type', 'text/plain')
    res.set('Content-Type', 'text/html')
    res.set('Content-Type', 'application/json')
    res.set('Content-Type', 'application/pdf')
    res.set('xxx-code', '520')  // 可以任意设置
    res.append(key , value)
    
    res.render( )    // 渲染模板
    res.redirect( 'https://google.com')  // 重定向请求
    res.download(path.join(_dirname, 'data.json' )) // 弹出文件下载
    res.sendFile(path.join(__dirname, 'public', 'index.html'))  // 返回文件
    
    res.type('application/json') // set Content-Type
    
    res.jsonp( )    // 返回 jsonp
    
    res.cookie(name [, options])  // cookie
    res.clearCookie(name [, options])

})

🚀cookie

npm install cookie-parser

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();

app.use(cookieParser());

app.get('/', (req, res) => {
    const allCookies = req.cookies;
    const token = req.cookies.token;
});

// set cookies
res.status(201).cookie('token', `${token}`, {
    expires: new Date(Date.now() + 8 * 3600000)  // cookie will be removed after 8 hours
})
.redirect(301, '/admin')
Property Type Description
domain String cookie 的域名。默认为应用程序的域名。
path String cookie 的路径。默认为“/”。
expires Date cookie 的过期日期(GMT 时间)。如果未指定或设置为 0,则创建会话 cookie。
maxAge Number 方便地设置相对于当前时间的过期时间(以毫秒为单位)。
httpOnly Boolean 将此 cookie 标记为仅供 Web 服务器访问。
secure Boolean 将此 cookie 标记为仅用于 HTTPS。
signed Boolean 指示是否应该对 cookie 进行加密。
encode Function 用于 cookie 值编码的同步函数。默认为encodeURIComponent.
partitioned Boolean 表示该 Cookie 应使用分区存储。有关更多详细信息,请参阅“具有独立分区状态的 Cookie (CHIPS)” 。
priority String “Priority” Set-Cookie属性的值。
sameSite Boolean or String “SameSite” Set-Cookie属性的值。更多信息请访问https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1。

Tip

LocalStorage、Cookieの差異

LocalStorage
容量: 約5MB
保存期間: ユーザーが手動で削除するまで永続的に保存される
サーバー送信: 自動では送信されない(クライアントサイドのみで利用)
用途: アプリケーションの設定やユーザーカスタマイズデータなど、長期間保存したいデータに適している
★サーバ側で取得できないので、格納&取得はブラウザ側で実装する必要ある★

Cookie
容量: 約4KB
保存期間: 設定した有効期限まで保存される
サーバー送信: HTTPリクエストごとに自動的にサーバーに送信される
用途: ユーザー認証、セッション管理など、サーバーとのやり取りが必要なデータに適している

🚀Middleware in Express

Tip

app.use(express.json()); // 解析request里的 json, 放入res.body
app.use(express.urlencoded({ extended: true })); // 解析表单数据, 放入res.body
app.use(express.static('public')); // 静态文件
const router=express.Router(); // 生成路由

Common Third-party Middleware:

const mid = function (req, res, next) {    // ミドルウェア定義
    console.log({ msg: `${new Date()} ${req.method} ${req.originalUrl}` })
    next();
}
app.use(mid);  // ミドルウェア //  全局利用
app.get('/home', recordLogMiddleware, (req, res) => { }) // 局部利用
app.get('/setting', checkCodeMiddleware, (req, res) => { }) // 局部利用
// Authentication middleware
function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;
  
  if (!authHeader) {
    return res.status(401).send('Authentication required');
  }
  
  const token = authHeader.split(' ')[1];
  
  // Verify the token (simplified)
  if (token === 'secret-token') {
    // Authentication successful
    req.user = { id: 123, username: 'john' };
    next();
  } else {
    res.status(403).send('Invalid token');
  }
}

// Apply to specific routes
app.get('/api/protected', authenticate, (req, res) => {
  res.json({ message: 'Protected data', user: req.user });
});

🚀Serving Static Files

app.use('/assets', express.static(path.join(__dirname, 'public'))),来访问 public 目录下的文件时需要加上 /static 前缀,如 http://localhost:5000/assets/style.css

app.use('/assets/css', express.static(path.join(__dirname, 'public'))),来访问 public 目录下的文件时需要加上 /static 前缀,如 http://localhost:5000/assets/css/style.css


app.use(express.static(path.join(__dirname, 'public'), options))
const options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

🚀Error Handling in Express

app.use((err, req, res, next) => {     // 捕捉err
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
app.get('/error', (req, res) => {      // Route that may throw an error
  // Simulating an error
  throw new Error('Something went wrong!');    // 抛出err
});


app.get('/async-error', (req, res, next) => {    // Route that uses next(error) for asynchronous code
  // Simulating an asynchronous operation that fails
  setTimeout(() => {
    try {
      // Something that might fail
      const result = nonExistentFunction(); // This will throw an error
      res.send(result);
    }
    catch (error) {
      next(error);                          // 抛出err
    }
    }, 100);
});


app.use(errorHandle);
 
function errorHandle(err, req, res, next){ // 捕捉err
  console.error(err.stack);
  res.status(500).send('Something broke!');
}

🚀Example: Recommended Order

// 1. Application-wide middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(helmet());
app.use(cors());


// 2. Route-specific middleware
app.use('/api', authenticate);

// 3. Routes
app.use('/api/users', userRoutes);
app.use('/api/products', productRoutes);

// 4. 404 handler
app.use((req, res) => {
  res.status(404).json({ message: 'Not found' });
});

// 5. Error handler (always last)
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ message: 'Server error' });
});

🚀Routing in Separate Files

routes/users.js

const express = require('express');
const router = express.Router();

// Middleware specific to this router
router.use((req, res, next) => {
  console.log('Users Router Time:', Date.now());
  next();
});

// Define routes
router.get('/', (req, res) => {
  res.send('Users home page');
});

router.get('/:id', (req, res) => {
  res.send(`User profile for ID: ${req.params.id}`);
});

module.exports = router;
routes/products.js

const express = require('express');
const router = express.Router();

// Define routes
router.get('/', (req, res) => {
  res.send('Products list');
});

router.get('/:id', (req, res) => {
  res.send(`Product details for ID: ${req.params.id}`);
});

module.exports = router;
app.js (main file)

const express = require('express');
const usersRouter = require('./routes/users');    //★
const productsRouter = require('./routes/products');    //★

const app = express();
const port = 8080;

// Use the routers
app.use('/users', usersRouter);    //★
app.use('/products', productsRouter);    //★

app.get('/', (req, res) => {
  res.send('Main application home page');
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

🚀 进阶写法(Security)

https://www.w3schools.com/nodejs/nodejs_https.asp

.env file:

NODE_ENV=development PORT=3000
HOST=0.0.0.0
SSL_KEY_PATH=./key.pem
SSL_CERT_PATH=./cert.pem
const express = require('express');
const https = require('https');
const fs = require('fs');
const path = require('path');
const helmet = require('helmet');   // ★Security middleware
const cors = require('cors');

const app = express();

// Security middleware
app.use(helmet());  // 「app.use(helmet())」と指定すれば、デフォルトの設定がされます。

// CORS configuration
app.use(cors({
  origin: 'https://example.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));


// Parse JSON and URL-encoded bodies
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Serve static files from 'public' directory
app.use(express.static(path.join(__dirname, 'public'), {
  dotfiles: 'ignore',
  etag: true,
  extensions: ['html', 'htm'],
  index: 'index.html',
  maxAge: '1d',
  redirect: true
}));


// Routes
app.get('/', (req, res) => {
  res.send('<h1>Welcome to Secure Express Server</h1>');
});

app.get('/api/status', (req, res) => {
  res.json({
    status: 'operational',
    timestamp: new Date().toISOString(),
    environment: process.env.NODE_ENV || 'development',
    nodeVersion: process.version
  });
});

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

// 404 handler
app.use((req, res) => {
  res.status(404).json({ error: 'Not Found' });
});

// SSL/TLS options
const sslOptions = {
  key: fs.readFileSync(path.join(__dirname, 'key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
  // Enable HTTP/2 if available
  allowHTTP1: true,
  // Recommended security options
  minVersion: 'TLSv1.2',
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
    'TLS_AES_128_GCM_SHA256',
    'ECDHE-RSA-AES128-GCM-SHA256',
    '!DSS',
    '!aNULL',
    '!eNULL',
    '!EXPORT',
    '!DES',
    '!RC4',
    '!3DES',
    '!MD5',
    '!PSK'
  ].join(':'),
  honorCipherOrder: true
};

// Create HTTPS server
const PORT = process.env.PORT || 3000;
const server = https.createServer(sslOptions, app);

// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  // Perform cleanup and exit if needed
  process.exit(1);
});

// Graceful shutdown
const gracefulShutdown = (signal) => {
  console.log(`\nReceived ${signal}. Shutting down gracefully...`);

  server.close(() => {
    console.log('HTTP server closed.');
    // Close database connections, etc.
    process.exit(0);
  });

  // Force close server after 10 seconds
  setTimeout(() => {
    console.error('Forcing shutdown...');
    process.exit(1);
  }, 10000);
};

// Listen for shutdown signals
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);

// Start the server
const HOST = process.env.HOST || '0.0.0.0';
server.listen(PORT, HOST, () => {
  console.log(`Express server running at https://${HOST}:${PORT}`);
  console.log('Environment:', process.env.NODE_ENV || 'development');
  console.log('Press Ctrl+C to stop the server');
});

実例1

const data = require('./data.json');   // 导入json对象
const express = require('express');
const app = express();
const PORT = 80;

app.get('/', (req, res) => {
    let html = ""
    data.map(item => {                   // 同过map()遍历
        html += `<li>${item.name}</li><img src=${item.message}/>`
    })
    res.send(html);
})

app.get('/:id', (req, res) => {
    let html = ""
    let item = data.find( (item) =>  item.id == req.params.id );     // 同过find() 匹配
    if (item) {
        html += `<li>${item.name}</li><img src=${item.message} />`
    } else {
        html += `<h1>404 Not Found</h1>`
    }
    res.send(html);
})

app.listen(PORT, () => {
    console.log(`server is starting on ${PORT} `);
})
[
    {
        "id": 1,
        "name": "weimaraner",
        "message": "https://images.dog.ceo/breeds/weimaraner/n02092339_4214.jpg",
        "status": "success"
    },
    {
        "id": 2,
        "name": "dane-great",
        "message": "https://images.dog.ceo/breeds/dane-great/n02109047_5936.jpg",
        "status": "success"
    },
    {
        "id": 3,
        "name": "pyrenees",
        "message": "https://images.dog.ceo/breeds/pyrenees/n02111500_4731.jpg",
        "status": "success"
    }
]

実例2

const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
const port = 80;

function accessLog(req, res, next) {     // access.log 全局中间件
    let { url, ip } = req;
    let now = new Date();
    let year = now.getFullYear(); // 获取四位年份
    let month = now.getMonth() + 1; // 月份从0开始,所以+1
    let day = now.getDate(); // 日期
    let hours = now.getHours(); // 小时
    let minutes = now.getMinutes(); // 分钟
    let seconds = now.getSeconds(); // 秒

    // 格式化为 YYYY-MM-DD HH:mm:ss
    let formattedTime = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')} ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;

    fs.appendFileSync(path.resolve(__dirname, './access.log'), `${formattedTime} ${ip} ${url}\r\n`);
    next();
}

app.use(accessLog);

app.get('/', (req, res) => {
    res.send(`${req.ip} ${req.get("user-agent")}`);
});


app.get('/list', (req, res) => {
    fetch('https://dummyjson.com/recipes')
        .then(response => response.json())
        .then(data => {
            console.log(data.recipes);
            res.json(data.recipes);
        })
        .catch(error => console.log(error))
});

app.get('/list/:id', (req, res) => {
    let id = req.params.id;
    fetch('https://dummyjson.com/recipes')
        .then(response => response.json())
        .then(data => {
            const item = data.recipes.find(item => item.id == id);
            console.log(item);
            res.json(item);
        })
        .catch(error => console.log(error))
});


app.listen(port, () => {
    console.log('serve in on port 80');
})

🚀Express 项目架构

my-express-app/
├── node_modules/ # Dependencies
├── config/ # Configuration files
│ ├── db.js # Database configuration
│ └── env.js # Environment variables
├── controllers/ # Route controllers
├── models/ # Database models
├── routes/ # Route definitions
├── middleware/ # Custom middleware
├── public/ # Static files
├── tests/ # Test files
├── .env # Environment variables
├── .gitignore # Git ignore file
├── app.js # Application entry point
└── package.json # Project configuration

// app.js
import path from 'path';
const __dirname = import.meta.dirname;

import userRouter from './routes/userRouter.js';
import logPrint from './utils/logger.js';
import errorHandler from './utils/errorHandler.js';

import express from 'express';
const app = express();

app.use(errorHandler);
app.use(logPrint);
app.use(express.json());
app.use('/api/users', userRouter);

app.use((req, res) => {
    console.log(path.join(__dirname, '../public', '404.html'));
    res.status(404).sendFile(path.join(__dirname, '../public', '404.html'));
})

app.listen(3000, () => {
    console.log('Server is running on 3000');
})
// routes/userRouter.js
import express from 'express';
const userRouter = express.Router();
import { getUsers, getUserById, updateUser, deleteUser, createUser, searchUser } from '../controllers/userController.js';

userRouter.get('/', getUsers);
userRouter.get('/:id', getUserById);
userRouter.post('/', createUser);
userRouter.put('/:id', updateUser);
userRouter.delete('/:id', deleteUser);

// Filtering and pagination
userRouter.get('/search', searchUser); ///search?category=electronics&sort=price&limit=10&page=2'

export default userRouter;```

```javascript
// controllers/userController.js
import User from '../models/User.js';

async function getUsers(req, res) {
  try {
    const users = await User.findAll();
    if (!users) {
      return res.status(404).json({ message: 'User not found' });
    }
    res.status(200).json(users);
  } catch (error) {
    res.status(500).json({ message: 'Error retrieving users', error: error.message });
  }
};

async function getUserById(req, res) {
  try {
    const user = await User.findById(req.params.id);
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }
    res.status(200).send(user);
  } catch (error) {
    res.status(500).json({ message: 'Error retrieving user', error: error.message });
  }
};

async function updateUser(req, res) {
  try {
    const user = await User.updateById(req.params.id, req.body);
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }
    res.status(200).send(user);
  } catch (error) {
    res.status(500).json({ message: 'Error retrieving user', error: error.message });
  }
};

async function deleteUser(req, res) {
  try {
    // const user = await User.deleteById(req.params.id);
    // if (!user) {
    //   return res.status(404).json({ message: 'User not found' });
    // }
    res.status(200).send('deleteUser');

  } catch (error) {
    res.status(500).json({ message: 'Error retrieving user', error: error.message });
  }
};

async function createUser(req, res) {
  try {
    // const user = await User.create(req.body);
    res.status(200).send('createUser');
  } catch (error) {
    res.status(400).json({ message: 'Error creating user', error: error.message });
  }
};

export { getUsers, getUserById, updateUser, deleteUser, createUser, searchUser  };
// models/User.js
import data from "./data.json" with { type: "json" };

const findAll = () => data;
const findById = (id) => data.find(u => u.id === parseInt(id));
const updateById = (id, userInfo) => {
    const user = data.find(u => u.id === parseInt(id))
    if (user) {
        console.log(user);
        if (userInfo.name) user.name = userInfo.name;
        if (userInfo.message) user.message = userInfo.message;
        if (userInfo.status) user.status = userInfo.status;
        console.log(user);
        return user;
    }
}
const deleteById = () => data;
const create = () => data;

const User = {
    findAll: findAll,
    findById: findById,
    updateById: updateById,
    deleteById: deleteById,
    create: create
}

export default User;

##🚀Error Handling

// utils/errorHandler.js
class AppError extends Error {
  constructor(statusCode, message) {
    super(message);
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    this.isOperational = true;

    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = { AppError };

// middleware/errorMiddleware.js
const errorHandler = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';

  // Different error responses for development and production
  if (process.env.NODE_ENV === 'development') {
    res.status(err.statusCode).json({
      status: err.status,
      message: err.message,
      stack: err.stack,
      error: err
    });
  } else {
    // Production: don't leak error details
    if (err.isOperational) {
      res.status(err.statusCode).json({
        status: err.status,
        message: err.message
      });
    } else {
      // Programming or unknown errors
      console.error('ERROR 💥', err);
      res.status(500).json({
        status: 'error',
        message: 'Something went wrong'
      });
    }
  }
};

module.exports = { errorHandler };

// Usage in app.js
const { errorHandler } = require('./middleware/errorMiddleware');
const { AppError } = require('./utils/errorHandler');

// This route throws a custom error
app.get('/api/error-demo', (req, res, next) => {
  next(new AppError(404, 'Resource not found'));
});

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

🚀API Documentation

const express = require('express');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const app = express();

// Swagger configuration
const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'User API',
      version: '1.0.0',
      description: 'A simple Express User API'
    },
    servers: [
      {
        url: 'http://localhost:8080',
        description: 'Development server'
      }
    ]
  },
  apis: ['./routes/*.js'] // Path to the API routes folders
};

const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));

/**
* @swagger
* /api/users:
* get:
* summary: Returns a list of users
* description: Retrieve a list of all users
* responses:
* 200:
* description: A list of users
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* name:
* type: string
* email:
* type: string
*/
app.get('/api/users', (req, res) => {
  // Handler implementation
});

app.listen(8080);

🚀Testing APIs

// tests/users.test.js
const request = require('supertest');
const app = require('../app');

describe('User API', () => {
  describe('GET /api/users', () => {
    it('should return all users', async () => {
      const res = await request(app).get('/api/users');
      expect(res.statusCode).toBe(200);
      expect(Array.isArray(res.body)).toBeTruthy();
    });
  });
  describe('POST /api/users', () => {
    it('should create a new user', async () => {
      const userData = {
        name: 'Test User',
        email: '[email protected]'
      };
      const res = await request(app)
        .post('/api/users')
        .send(userData);

      expect(res.statusCode).toBe(201);
      expect(res.body).toHaveProperty('id');
      expect(res.body.name).toBe(userData.name);
    });
    it('should validate request data', async () => {
      const invalidData = {
        email: 'not-an-email'
      };
      const res = await request(app)
        .post('/api/users')
        .send(invalidData);

      expect(res.statusCode).toBe(400);
    });
  });
});

🚀API Versioning

const express = require('express');
const app = express();

// Version 1 routes
const v1UserRoutes = require('./routes/v1/users');
app.use('/api/v1/users', v1UserRoutes);

// Version 2 routes with new features
const v2UserRoutes = require('./routes/v2/users');
app.use('/api/v2/users', v2UserRoutes);

app.listen(8080);

财产 类型 描述
domain 细绳 cookie 的域名。默认为应用程序的域名。
encode 功能 用于 cookie 值编码的同步函数。默认为encodeURIComponent.
expires 日期 cookie 的过期日期(GMT 时间)。如果未指定或设置为 0,则创建会话 cookie。
httpOnly 布尔值 将此 cookie 标记为仅供 Web 服务器访问。
maxAge 数字 方便地设置相对于当前时间的过期时间(以毫秒为单位)。
path 细绳 cookie 的路径。默认为“/”。
partitioned 布尔值 表示该 Cookie 应使用分区存储。有关更多详细信息,请参阅“具有独立分区状态的 Cookie (CHIPS)” 。
priority 细绳 “Priority” Set-Cookie属性的值。
secure 布尔值 将此 cookie 标记为仅用于 HTTPS。
signed 布尔值 指示是否应该对 cookie 进行签名。
sameSite 布尔值或字符串 “SameSite” Set-Cookie属性的值。更多信息请访问https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1。

❤️ 转载文章请注明出处,谢谢!❤️