RESTful API #
一、项目概述 #
1.1 项目结构 #
text
api-project/
├── src/
│ ├── index.js
│ ├── config/
│ │ └── index.js
│ ├── routes/
│ │ ├── index.js
│ │ ├── users.js
│ │ └── products.js
│ ├── handlers/
│ │ ├── users.js
│ │ └── products.js
│ ├── models/
│ │ ├── user.js
│ │ └── product.js
│ ├── services/
│ │ ├── userService.js
│ │ └── productService.js
│ ├── validations/
│ │ └── schemas.js
│ └── plugins/
│ ├── index.js
│ └── errorHandler.js
├── package.json
└── .env
1.2 安装依赖 #
bash
npm init -y
npm install @hapi/hapi @hapi/joi @hapi/boom @hapi/inert @hapi/vision
npm install dotenv
npm install -D nodemon
二、入口文件 #
2.1 src/index.js #
javascript
require('dotenv').config();
const Hapi = require('@hapi/hapi');
const config = require('./config');
const plugins = require('./plugins');
const routes = require('./routes');
const init = async () => {
const server = Hapi.server({
port: config.port,
host: config.host,
routes: {
cors: {
origin: ['*'],
headers: ['Accept', 'Content-Type', 'Authorization']
}
}
});
await server.register(plugins);
server.route(routes);
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.error(err);
process.exit(1);
});
init();
2.2 src/config/index.js #
javascript
module.exports = {
port: parseInt(process.env.PORT, 10) || 3000,
host: process.env.HOST || 'localhost',
env: process.env.NODE_ENV || 'development'
};
三、数据模型 #
3.1 src/models/user.js #
javascript
const users = [];
let nextId = 1;
module.exports = {
findAll: () => {
return users;
},
findById: (id) => {
return users.find(u => u.id === parseInt(id));
},
findByEmail: (email) => {
return users.find(u => u.email === email);
},
create: (data) => {
const user = {
id: nextId++,
...data,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
users.push(user);
return user;
},
update: (id, data) => {
const index = users.findIndex(u => u.id === parseInt(id));
if (index === -1) return null;
users[index] = {
...users[index],
...data,
updatedAt: new Date().toISOString()
};
return users[index];
},
delete: (id) => {
const index = users.findIndex(u => u.id === parseInt(id));
if (index === -1) return false;
users.splice(index, 1);
return true;
}
};
四、验证规则 #
4.1 src/validations/schemas.js #
javascript
const Joi = require('joi');
const userId = Joi.object({
id: Joi.number().integer().positive().required()
});
const createUser = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required()
});
const updateUser = Joi.object({
name: Joi.string().min(2).max(50),
email: Joi.string().email()
}).min(1);
const pagination = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(10)
});
module.exports = {
userId,
createUser,
updateUser,
pagination
};
五、处理函数 #
5.1 src/handlers/users.js #
javascript
const Boom = require('@hapi/boom');
const User = require('../models/user');
const getAll = async (request, h) => {
const { page, limit } = request.query;
const users = User.findAll();
const start = (page - 1) * limit;
const end = start + limit;
return {
users: users.slice(start, end),
total: users.length,
page,
limit
};
};
const getById = async (request, h) => {
const user = User.findById(request.params.id);
if (!user) {
throw Boom.notFound('User not found');
}
return user;
};
const create = async (request, h) => {
const existingUser = User.findByEmail(request.payload.email);
if (existingUser) {
throw Boom.conflict('Email already exists');
}
const user = User.create(request.payload);
return h.response(user).code(201);
};
const update = async (request, h) => {
const user = User.update(request.params.id, request.payload);
if (!user) {
throw Boom.notFound('User not found');
}
return user;
};
const remove = async (request, h) => {
const deleted = User.delete(request.params.id);
if (!deleted) {
throw Boom.notFound('User not found');
}
return h.response().code(204);
};
module.exports = {
getAll,
getById,
create,
update,
remove
};
六、路由定义 #
6.1 src/routes/users.js #
javascript
const handlers = require('../handlers/users');
const schemas = require('../validations/schemas');
module.exports = [
{
method: 'GET',
path: '/users',
options: {
description: 'Get all users',
tags: ['api'],
validate: {
query: schemas.pagination
}
},
handler: handlers.getAll
},
{
method: 'GET',
path: '/users/{id}',
options: {
description: 'Get user by ID',
tags: ['api'],
validate: {
params: schemas.userId
}
},
handler: handlers.getById
},
{
method: 'POST',
path: '/users',
options: {
description: 'Create new user',
tags: ['api'],
validate: {
payload: schemas.createUser
}
},
handler: handlers.create
},
{
method: 'PUT',
path: '/users/{id}',
options: {
description: 'Update user',
tags: ['api'],
validate: {
params: schemas.userId,
payload: schemas.updateUser
}
},
handler: handlers.update
},
{
method: 'DELETE',
path: '/users/{id}',
options: {
description: 'Delete user',
tags: ['api'],
validate: {
params: schemas.userId
}
},
handler: handlers.remove
}
];
6.2 src/routes/index.js #
javascript
const userRoutes = require('./users');
module.exports = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
return { message: 'Welcome to API', version: '1.0.0' };
}
},
{
method: 'GET',
path: '/health',
handler: (request, h) => {
return { status: 'ok', timestamp: new Date().toISOString() };
}
},
...userRoutes
];
七、错误处理 #
7.1 src/plugins/errorHandler.js #
javascript
const errorHandler = {
name: 'error-handler',
register: async (server, options) => {
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
return h.response({
success: false,
error: {
code: error.output.statusCode,
message: error.message,
details: error.data
}
}).code(error.output.statusCode);
}
return h.continue;
});
}
};
module.exports = errorHandler;
7.2 src/plugins/index.js #
javascript
const errorHandler = require('./errorHandler');
module.exports = [
errorHandler
];
八、运行项目 #
8.1 package.json #
json
{
"name": "hapi-rest-api",
"version": "1.0.0",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
}
}
8.2 启动服务 #
bash
npm run dev
九、API测试 #
9.1 获取所有用户 #
bash
curl http://localhost:3000/users
9.2 创建用户 #
bash
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com","password":"password123"}'
9.3 获取单个用户 #
bash
curl http://localhost:3000/users/1
9.4 更新用户 #
bash
curl -X PUT http://localhost:3000/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"Jane Doe"}'
9.5 删除用户 #
bash
curl -X DELETE http://localhost:3000/users/1
十、总结 #
RESTful API要点:
| 功能 | HTTP方法 | 路径 |
|---|---|---|
| 获取列表 | GET | /users |
| 获取单个 | GET | /users/:id |
| 创建 | POST | /users |
| 更新 | PUT | /users/:id |
| 删除 | DELETE | /users/:id |
下一步,让我们学习用户认证系统!
最后更新:2026-03-28