新手求Node.js Express MongoDB REST API完整教程:含路由等核心功能
Hey there! As someone who’s built several Node.js/Express/MongoDB APIs from scratch, I’ll walk you through a complete, hands-on tutorial that covers every part you mentioned—from project setup to advanced relationships and a full authentication system. Let’s dive in!
First, let’s get your project off the ground:
- Initialize a new Node.js project:
npm init -y - Install core dependencies:
npm install express mongoose dotenv bcryptjs jsonwebtoken - Create a project structure like this:
your-api/ ├── src/ │ ├── config/ │ │ └── db.js │ ├── controllers/ │ ├── middleware/ │ ├── models/ │ ├── routes/ │ └── app.js └── .env
Connect to MongoDB
Add your MongoDB URI to .env (local or Atlas):
MONGODB_URI=mongodb://localhost:27017/your-api-db JWT_SECRET=your-super-secret-jwt-key
Create src/config/db.js to handle the connection:
const mongoose = require('mongoose'); require('dotenv').config(); const connectDB = async () => { try { const conn = await mongoose.connect(process.env.MONGODB_URI); console.log(`MongoDB Connected: ${conn.connection.host}`); } catch (error) { console.error(`Connection Error: ${error.message}`); process.exit(1); } }; module.exports = connectDB;
Initialize Express in src/app.js:
const express = require('express'); const connectDB = require('./config/db'); // Connect to database connectDB(); const app = express(); // Middleware to parse JSON requests app.use(express.json()); // Mount routes (we'll create these next) app.use('/api/users', require('./routes/userRoutes')); app.use('/api/posts', require('./routes/postRoutes')); app.use('/api/courses', require('./routes/courseRoutes')); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Routes define how your API responds to client requests. Use express.Router() to organize routes by resource:
Example User Routes (src/routes/userRoutes.js)
const express = require('express'); const router = express.Router(); const { registerUser, loginUser, getMe } = require('../controllers/userController'); const { protect } = require('../middleware/authMiddleware'); // Public routes router.post('/register', registerUser); router.post('/login', loginUser); // Protected route (requires authentication) router.get('/me', protect, getMe); module.exports = router;
- Route Parameters: Use
/:idto capture dynamic values (e.g.,router.get('/:id', getUserById)). Access it viareq.params.id. - Query Strings: Access filters like
/api/posts?category=techviareq.query.category.
Middleware are functions that run between the request and response. They can modify requests, validate data, handle errors, and more.
Custom Logging Middleware
Create src/middleware/logger.js:
const logger = (req, res, next) => { console.log(`${req.method} ${req.protocol}://${req.get('host')}${req.originalUrl}`); next(); // Pass control to the next middleware/route }; module.exports = logger;
Mount it in app.js with app.use(logger);
Error Handling Middleware
Add this to the bottom of app.js to catch all errors:
app.use((err, req, res, next) => { const statusCode = res.statusCode === 200 ? 500 : res.statusCode; res.status(statusCode); res.json({ message: err.message, stack: process.env.NODE_ENV === 'production' ? null : err.stack }); });
Authentication Middleware (Preview)
We’ll use this to protect routes later. Create src/middleware/authMiddleware.js:
const jwt = require('jsonwebtoken'); const User = require('../models/User'); const protect = async (req, res, next) => { let token; // Check for Bearer token in Authorization header if (req.headers.authorization?.startsWith('Bearer')) { try { token = req.headers.authorization.split(' ')[1]; const decoded = jwt.verify(token, process.env.JWT_SECRET); // Attach user to request (exclude password) req.user = await User.findById(decoded.id).select('-password'); next(); } catch (error) { res.status(401).json({ message: 'Not authorized: Token invalid' }); } } if (!token) { res.status(401).json({ message: 'Not authorized: No token' }); } }; module.exports = { protect };
Mongoose lets you define schemas and handle MongoDB relationships. Let’s cover one-to-many and many-to-many associations.
One-to-Many: User → Posts
A user can have multiple posts, but a post belongs to one user.
Create src/models/User.js:
const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); const userSchema = new mongoose.Schema({ name: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true } }); // Hash password before saving userSchema.pre('save', async function(next) { if (!this.isModified('password')) return next(); const salt = await bcrypt.genSalt(10); this.password = await bcrypt.hash(this.password, salt); }); // Method to compare passwords userSchema.methods.matchPassword = async function(enteredPassword) { return await bcrypt.compare(enteredPassword, this.password); }; module.exports = mongoose.model('User', userSchema);
Create src/models/Post.js:
const mongoose = require('mongoose'); const postSchema = new mongoose.Schema({ title: { type: String, required: true }, content: { type: String, required: true }, user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', // Reference the User model required: true } }, { timestamps: true }); module.exports = mongoose.model('Post', postSchema);
Query with Population
To fetch posts with their associated user data:
const getPosts = async (req, res) => { const posts = await Post.find().populate('user', 'name email'); // Only return name/email res.status(200).json(posts); };
Many-to-Many: Users ↔ Courses
A user can enroll in multiple courses, and a course can have multiple students.
Update src/models/User.js to add a courses array:
const userSchema = new mongoose.Schema({ // ... existing fields courses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }] });
Create src/models/Course.js:
const mongoose = require('mongoose'); const courseSchema = new mongoose.Schema({ title: { type: String, required: true }, description: { type: String, required: true }, students: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] }); module.exports = mongoose.model('Course', courseSchema);
Enroll a Student in a Course
Create a controller function to handle the bidirectional association:
const enrollStudent = async (req, res) => { const { courseId, userId } = req.body; try { // Add course to user's enrollment list await User.findByIdAndUpdate(userId, { $push: { courses: courseId } }); // Add user to course's student list await Course.findByIdAndUpdate(courseId, { $push: { students: userId } }); res.status(200).json({ message: 'Student enrolled successfully' }); } catch (error) { res.status(500).json({ message: error.message }); } };
Let’s build a full login/register system with JWT tokens.
User Controller (src/controllers/userController.js)
const User = require('../models/User'); const jwt = require('jsonwebtoken'); // Generate JWT token const generateToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d' }); }; // Register a new user const registerUser = async (req, res) => { const { name, email, password } = req.body; // Check if user already exists const userExists = await User.findOne({ email }); if (userExists) { return res.status(400).json({ message: 'User already exists' }); } // Create user const user = await User.create({ name, email, password }); if (user) { res.status(201).json({ _id: user._id, name: user.name, email: user.email, token: generateToken(user._id) }); } else { res.status(400).json({ message: 'Invalid user data' }); } }; // Login user const loginUser = async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (user && (await user.matchPassword(password))) { res.json({ _id: user._id, name: user.name, email: user.email, token: generateToken(user._id) }); } else { res.status(401).json({ message: 'Invalid email or password' }); } }; // Get current logged-in user const getMe = async (req, res) => { res.status(200).json(req.user); }; module.exports = { registerUser, loginUser, getMe };
Use tools like Postman or curl to test endpoints:
- Register: POST
http://localhost:5000/api/users/registerwithname,email,passwordin the request body. - Login: POST
http://localhost:5000/api/users/loginwithemailandpasswordto get a token. - Protected Route: GET
http://localhost:5000/api/users/mewith aBearer <token>header to access your user data.
内容的提问来源于stack exchange,提问作者tapos ghosh

