How to Create a Login System with Node.js and Express
Creating a login system is a fundamental skill for any web developer. In this guide, we'll walk through the process of building a secure login system using Node.js and Express. This tutorial will cover everything from setting up your environment to implementing user authentication and error handling.
What is Node.js and Express?
Node.js is a JavaScript runtime built on Chrome's V8 engine that allows developers to execute JavaScript on the server side. It’s widely used for building scalable network applications due to its non-blocking, event-driven architecture.
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It simplifies the development of server-side applications and APIs.
Use Cases for a Login System
A login system is essential for:
- User Authentication: Ensure that only authorized users access certain resources.
- Personalization: Customize user experience based on user profiles.
- Data Security: Protect sensitive information from unauthorized access.
Prerequisites
Before we start coding, ensure you have the following installed:
- Node.js: Download and install from Node.js official website.
- npm: Node package manager, comes with Node.js.
- A code editor (like Visual Studio Code).
- Basic knowledge of JavaScript and Express.
Step 1: Setting Up Your Project
Create a new directory for your project and navigate into it using your terminal:
mkdir login-system
cd login-system
Initialize a new Node.js project:
npm init -y
Install necessary packages:
npm install express mongoose bcryptjs express-session connect-mongo dotenv
express
: For creating the server.mongoose
: For interacting with MongoDB.bcryptjs
: For hashing passwords.express-session
: For managing user sessions.connect-mongo
: For storing sessions in MongoDB.dotenv
: For managing environment variables.
Step 2: Creating the Server
Create a file named server.js
:
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const MongoStore = require('connect-mongo');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
}).then(() => console.log("MongoDB connected"))
.catch(err => console.log(err));
app.use(express.urlencoded({ extended: true }));
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGODB_URI }),
}));
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Explanation
- We import the required packages and configure the server.
- We connect to a MongoDB database using Mongoose.
- We set up session management with
express-session
and store sessions in MongoDB.
Step 3: Creating the User Model
Create a new folder named models
and a file User.js
inside it:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
userSchema.methods.comparePassword = function(password) {
return bcrypt.compare(password, this.password);
};
module.exports = mongoose.model('User', userSchema);
Explanation
- We create a user schema with username and password fields.
- We hash passwords before saving them to the database using
bcryptjs
.
Step 4: Implementing Registration and Login Routes
Create a folder named routes
and a file auth.js
inside it:
const express = require('express');
const User = require('../models/User');
const router = express.Router();
router.post('/register', async (req, res) => {
try {
const { username, password } = req.body;
const newUser = new User({ username, password });
await newUser.save();
res.status(201).send('User registered successfully!');
} catch (err) {
res.status(400).send('Error registering user');
}
});
router.post('/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user || !(await user.comparePassword(password))) {
return res.status(401).send('Invalid credentials');
}
req.session.userId = user._id;
res.send('Login successful!');
} catch (err) {
res.status(400).send('Error logging in');
}
});
module.exports = router;
Explanation
- Two routes are implemented:
/register
for user registration and/login
for user authentication. - Passwords are validated, and sessions are created upon successful login.
Step 5: Integrating Routes into Your Server
In server.js
, import and use the routes:
const authRoutes = require('./routes/auth');
app.use('/auth', authRoutes);
Step 6: Testing Your Login System
Run your server:
node server.js
You can test your login system using tools like Postman or Insomnia.
-
Register a user: Send a POST request to
http://localhost:3000/auth/register
with a JSON body:json { "username": "testuser", "password": "password123" }
-
Login: Send a POST request to
http://localhost:3000/auth/login
with:json { "username": "testuser", "password": "password123" }
Conclusion
Congratulations! You have successfully created a basic login system with Node.js and Express. This system can be expanded with features such as password recovery, email verification, and user roles.
By understanding the core components and flow of a login system, you can build secure, user-friendly applications. Remember to always keep security in mind, especially when handling user data. Happy coding!