Building an API with Express.js and Connecting It to the Frontend
When building modern web applications, APIs serve as the bridge between the backend (server-side logic) and the frontend (user interface). In this blog post, we’ll break down how an Express.js-based API works, its key components, and how it connects to a frontend application.
1. What is a Router in Express.js?
A router in Express.js is a way to organize API endpoints into modular and reusable components. Think of it as a "sub-application" that groups related routes together.
-
Why Use Routers?
- Keeps code clean and modular.
- Allows you to separate concerns (e.g., authentication routes in
auth.js
, product routes inproducts.js
).
Example:
const express = require('express');
const router = express.Router();
// Define routes
router.post('/register', (req, res) => {
res.send('Register endpoint');
});
router.post('/login', (req, res) => {
res.send('Login endpoint');
});
// Export the router
module.exports = router;
This router can then be mounted in the main app:
const authRouter = require('./routes/auth');
app.use('/api/auth', authRouter);
2. Backend API Workflow
Here’s how a typical authentication API works:
a. POST /api/auth/register
- Purpose: Registers a new user.
-
Steps:
- Check if the email already exists in the database.
- Hash the password using
bcrypt
. - Save the user to the database.
- Return a JWT token for future authenticated requests.
b. POST /api/auth/login
- Purpose: Logs in an existing user.
-
Steps:
- Verify if the email exists.
- Compare the provided password with the hashed password stored in the database.
- If valid, return a JWT token.
c. GET /api/auth/user
- Purpose: Fetches data of the logged-in user.
-
Steps:
- Validate the JWT token using middleware.
- Return user data, excluding sensitive information like passwords.
Code Example for /register
:
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('./models/User'); // Mongoose model
router.post('/register', async (req, res) => {
const { email, password } = req.body;
// Check if user exists
const existingUser = await User.findOne({ email });
if (existingUser) return res.status(400).send('User already exists');
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Save user to database
const newUser = new User({ email, password: hashedPassword });
await newUser.save();
// Generate JWT token
const token = jwt.sign({ id: newUser._id }, 'secretKey', { expiresIn: '1h' });
res.status(201).json({ token });
});
3. Connecting the Backend to the Frontend
The frontend communicates with this API through HTTP requests.
Step 1: Registration/Login
The frontend sends a POST
request to /api/auth/register
or /api/auth/login
with user credentials.
Example Frontend Code:
// Register a user
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]', password: '123456' }),
});
const data = await response.json();
console.log(data.token); // JWT token
Step 2: Storing JWT Token
After receiving the JWT token, store it securely (e.g., in localStorage
or cookies):
localStorage.setItem('token', data.token);
Step 3: Making Authenticated Requests
For protected routes like /api/auth/user
, include the JWT token in the request headers:
const token = localStorage.getItem('token');
const response = await fetch('/api/auth/user', {
headers: { Authorization: `Bearer ${token}` },
});
const userData = await response.json();
console.log(userData);
4. Key Components of an Express API
Component | Role |
---|---|
Express Router | Organizes routes into modules for cleaner code. |
Bcrypt | Hashes passwords securely before storing them in a database. |
JWT (JSON Web Token) | Generates tokens for authenticating users across sessions. |
Mongoose Models | Defines schemas for MongoDB collections (e.g., users). |
Auth Middleware | Validates JWT tokens for protected routes like /api/auth/user . |
Summary: Frontend vs Backend Roles
Frontend | Backend |
---|---|
Sends HTTP requests to API endpoints. | Handles requests and sends responses with data/logic. |
Stores and uses tokens for auth. | Generates and validates tokens using JWTs. |
Displays user data (e.g., profile). | Fetches and secures data from databases like MongoDB. |
Why This Matters
- Security: Passwords are hashed, and JWT ensures only authenticated users access protected routes.
- Scalability: Modular routing and middleware make it easier to grow your application.
- Separation of Concerns: The frontend focuses on UI/UX while the backend handles logic and data.
This approach forms the backbone of most modern web apps, enabling seamless communication between frontend interfaces and backend systems.
Tags:
#NodeJS
#ExpressJS
#API
#Authentication
#WebDevelopment
#FrontendBackendIntegration
Citations:
[1] https://rapidapi.com/blog/nodejs-express-rest-api-example/
[2] https://www.freecodecamp.org/news/the-express-handbook/
[3] https://dev.to/mrrishimeena/understanding-expressjs-router-nodejs-a-comprehensive-guide-59o8
[4] https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction
[5] https://dev.to/reiallenramos/create-an-express-api-static-and-dynamic-routes-33lb
[6] https://www.codecademy.com/article/what-is-express-js
[7] https://www.youtube.com/watch?v=1XxQBXl4Lv8
[8] https://www.freecodecamp.org/news/rest-api-design-best-practices-build-a-rest-api/
[9] https://www.topcoder.com/thrive/articles/routing-in-express-js
[10] https://kinsta.com/knowledgebase/what-is-express-js/