Introduction
In this blog, we will explore a simple user authentication system built using Node.js, Express, and PostgreSQL. This example demonstrates how to handle user registration and login functionality by interacting with a database.
The Code Breakdown
Let's break down the code step by step:
1. Setting Up Dependencies
import express from "express";
import bodyParser from "body-parser";
import pg from "pg";
Express: A lightweight framework used to build web applications in Node.js. It simplifies routing and server setup.
Body-Parser: A middleware that helps parse incoming request bodies. In this case, it's used to parse URL-encoded data from forms.
pg (PostgreSQL): A PostgreSQL client for Node.js, enabling interaction with the PostgreSQL database.
2. Creating the Express App
const app = express();
const port = 3000;
app: This is the Express application that will handle HTTP requests.
port: Specifies the port number (3000) the server will listen on.
3. Connecting to the PostgreSQL Database
const db = new pg.Client({
user: "postgres",
host: "localhost",
database: "secrets",
password: "mspostgres",
port: 5432,
});
db.connect();
pg.Client: Creates a new client to connect to the PostgreSQL database. The details like user, host, database, password, and port are passed in to establish the connection.
db.connect(): This connects to the database.
4. Middleware Setup
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
bodyParser.urlencoded: This middleware parses URL-encoded form data, making it accessible in
req.body
.express.static: This middleware serves static files (like images, CSS, JS) from the "public" directory.
5. Routing: Display Pages
app.get("/", (req, res) => {
res.render("home.ejs");
});
app.get("/login", (req, res) => {
res.render("login.ejs");
});
app.get("/register", (req, res) => {
res.render("register.ejs");
});
These routes handle GET requests and render EJS templates:
home.ejs: Displays the home page.
login.ejs: Displays the login form.
register.ejs: Displays the registration form.
6. User Registration (POST Request)
app.post("/register", async (req, res) => {
const email = req.body.username;
const password = req.body.password;
try {
const checkResult = await db.query("SELECT * FROM users WHERE email = $1", [
email,
]);
if (checkResult.rows.length > 0) {
res.send("Email already exists. Try logging in.");
} else {
await saveUser(email, password);
res.render("login.ejs");
}
} catch (err) {
console.log(err);
}
});
POST /register: This route is triggered when the registration form is submitted.
Check for Existing Email: Before saving a new user, the code now checks if the email already exists in the database.
Handling Existing Email: If the email already exists, the user is informed with the message "Email already exists. Try logging in."
Redirect to Login Page: If the email is new, the user details are saved, and the user is redirected to the login page (
login.ejs
) for them to log in.This ensures that users cannot register with an email that's already in use, improving the user experience by guiding them to log in instead.7. User Login (POST Request)
app.post("/login", async (req, res) => {
const email = req.body.username;
const password = req.body.password;
try {
const result = await db.query("SELECT * FROM users WHERE email = $1", [email]);
if (result.rows.length > 0) {
const user = result.rows[0];
if (password === user.password) {
res.render("secrets.ejs");
} else {
res.status(400).send("Incorrect password.");
}
} else {
res.status(400).send("User not found.");
}
} catch (err) {
console.error("Error during login:", err);
res.status(500).send("Error during login.");
}
});
POST /login: This route is triggered when the login form is submitted.
It retrieves the email and password from
req.body
.It queries the database for a user with the provided email.
If a matching user is found, it checks if the entered password matches the stored password.
If both conditions are met, the user is redirected to secrets.ejs. If not, an error message is displayed.
8. Saving User to the Database
const saveUser = async (email, password) => {
try {
const query = `
INSERT INTO users (email, password)
VALUES ($1, $2);
`;
await db.query(query, [email, password]);
console.log("User saved successfully.");
} catch (err) {
console.error("Error saving user:", err);
}
};
saveUser(): This function inserts the user's email and password into the
users
table of the PostgreSQL database.It uses an SQL query to insert the values into the database.
Any errors during the process are caught and logged.
9. Starting the Server
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
- app.listen(): This starts the server and listens on the specified port (3000). Once the server is up, a message is logged in the console.
Conclusion
This basic user authentication system demonstrates how to register and log in users using Express and PostgreSQL. The system validates user credentials, saves user information to the database, and displays corresponding pages based on the user's authentication status. For further enhancement, you can add password hashing, sessions, and other security measures for a more robust authentication system.