This guide will walk you through setting up a Node.js and Express API with MongoDB, Mongoose, and JWT authentication. Follow the steps below to build your project! π οΈ
-
Create a New Node.js Project: Run
npm init -yand install dependencies likeexpress,mongoose,body-parser,cookie-parser,cors, andhelmetusing:npm install express mongoose body-parser cookie-parser cors helmet
-
Create a Basic Server: In
server.js, set up the server to listen on a port and add a simple route (e.g.,GET /) that sends back a response like"Hello World! π".const express = require("express"); const app = express(); const port = 3000; app.get("/", (req, res) => { res.send("Hello World! π"); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
-
Set Up Middleware: Use
body-parserto parse request bodies,cookie-parserfor handling cookies,corsto allow cross-origin requests, andhelmetfor security.app.use(bodyParser.json()); app.use(cookieParser()); app.use(cors()); app.use(helmet());
-
Set Up Routes: Create separate route files (e.g.,
auth.routes.js,user.routes.js) and define routes likeGET /api/usersorPOST /api/users. Each route should handle basic CRUD operations. Example foruser.routes.js:const express = require("express"); const router = express.Router(); router.get("/api/users", (req, res) => { res.send("List of Users"); }); router.post("/api/users", (req, res) => { res.send("User created"); }); module.exports = router;
- Connect to MongoDB: Install
mongooseand usemongoose.connect()to connect to your MongoDB database. For local MongoDB, the URI will look like:mongoose.connect("mongodb://localhost:27017/yourdbname", { useNewUrlParser: true, useUnifiedTopology: true, });
- Create Models: Use Mongoose to define models (e.g.,
User) that represent your data structure. For example:const userSchema = new mongoose.Schema({ name: String, email: String, }); const User = mongoose.model("User", userSchema);
-
Create Logic to Handle Requests: Define controller functions for actions like
createUser,getUser,updateUser, which interact with the database and handle incoming requests. Example:const createUser = (req, res) => { const user = new User(req.body); user.save().then(() => res.status(201).send(user)); }; const getUser = (req, res) => { User.findById(req.params.id).then((user) => { if (!user) { return res.status(404).send("User not found"); } res.send(user); }); };
- Handle Errors Gracefully: Add error handling middleware to ensure the app doesnβt crash when something goes wrong. Example:
app.use((err, req, res, next) => { console.error(err); res.status(500).send("Something went wrong! π"); });
-
JWT Authentication: Install
jsonwebtoken:npm install jsonwebtoken
-
Set Up Routes for Login: Define a login route that will authenticate the user and return a JWT token:
const jwt = require("jsonwebtoken"); router.post("/login", (req, res) => { const token = jwt.sign({ userId: req.user._id }, "your_jwt_secret"); res.json({ token }); });
-
Create Middleware for JWT Verification: Protect routes by verifying JWT tokens. Example:
const verifyToken = (req, res, next) => { const token = req.headers["authorization"]; if (!token) { return res.status(403).send("Token required"); } jwt.verify(token, "your_jwt_secret", (err, decoded) => { if (err) { return res.status(401).send("Unauthorized"); } req.user = decoded; next(); }); };
- Use Postman: Test your API using Postman or any other API testing tool to ensure all routes are functioning as expected.
- Check for Edge Cases: Test edge cases like:
- Trying to fetch a non-existent user.
- Accessing protected routes without a token.
This Node.js project is a basic RESTful API built with Express, MongoDB, and JWT (JSON Web Tokens) for user authentication. Below is a breakdown of how the code works:
This file connects to MongoDB using Mongoose. It reads the MongoDB URI from the config and establishes the connection. If successful, it logs the success message; otherwise, it catches errors and logs them.
It imports the express.js file (which defines routes) and uses a simple template function to send a response when the home route ("/") is accessed.
It starts the Express server, listening on a port defined in the config.
This file configures several middlewares for the application:
- bodyParser: Parses incoming request bodies and attaches them to
req.body. - cookieParser: Parses cookies attached to the incoming requests.
- compress: Compresses the response data.
- helmet: Adds various security-related HTTP headers.
- cors: Enables Cross-Origin Resource Sharing for the application.
It imports the user.routes.js and auth.routes.js files and uses them for the API.
It catches unauthorized errors and other potential errors, returning appropriate JSON error messages.
These files define routes related to users and authentication. They use Express routers to manage the API routes for users.
/api/users: Handles GET and POST requests for listing all users and creating a new user./api/users/:userId: Handles GET, PUT, and DELETE requests to retrieve, update, or delete a user, with additional authentication checks (requireSigninandhasAuthorization).
This file handles the actual logic for creating, reading, updating, and deleting users:
- create: Creates a new user in the database.
- userByID: Loads a user by ID and attaches it to
req.profile. - read: Sends the user profile in the response.
- list: Returns a list of all users.
- update: Updates user information (excluding password-related fields).
- remove: Deletes a user from the database.
Uses the errorHandler to handle and format MongoDB-related errors.
This file handles user sign-in and sign-out using JWT:
- signin: Checks if the user exists and if the password matches. If successful, it generates a JWT token and returns it along with the user data.
- signout: Clears the JWT token (logs the user out).
- requireSignin: A middleware that checks if the user is signed in (valid JWT).
- hasAuthorization: A middleware that ensures the logged-in user is authorized to perform the action on the user they are trying to access.
This file defines the Mongoose schema for users, including name, email, password, and timestamps. The password field is a virtual property, meaning it doesn't exist in the database but allows you to set and get it.
It uses the crypto module to hash the password with a salt before storing it in the database.
It has an authenticate method that checks if the password provided matches the stored hashed password.
This file handles MongoDB-related errors, particularly for unique constraint violations (e.g., duplicate email) and general validation errors. It returns a human-readable error message for the response.
- Sign Up (POST /api/users): The user submits their details, and a new user is created in the database.
- Sign In (POST /api/signin): The user provides their credentials. If correct, a JWT token is generated, and the user is authenticated.
- Accessing User Data (GET /api/users/:userId): A request is made to retrieve a userβs data. The JWT token in the request is validated by the
requireSigninmiddleware. The user must also be authorized (checked byhasAuthorization) to access their profile data.
Happy coding! πβ¨