Building a Secure Login and Signup API with Node.js, PostgreSQL, and Modern Tools




When it comes to building a scalable API, implementing secure login and signup functionality is one of the most critical steps. Here’s a detailed walkthrough of how I set up a scalable and secure API using Node.js, PostgreSQL, bcrypt, and more. Along the way, I’ll share the exact steps, a few fun tidbits, and plenty of status codes to keep things professional (and functional). Let’s dive in! πŸš€


1. Setting Up the Project

Every great API starts with a solid foundation. Here’s how I got started:

Step 1.1: Initialize the Project

  1. Create a new directory for the project:

    mkdir login-signup-api && cd login-signup-api
    
  2. Initialize a new Node.js project:

    npm init -y
    

    This generates a package.json file with default settings.

  3. Install the required packages:

    npm install express pg bcrypt dotenv cors body-parser
    
    • express: For building the API.
    • pg: PostgreSQL client for Node.js.
    • bcrypt: For password hashing.
    • dotenv: To manage environment variables securely.
    • cors: To handle cross-origin requests.
    • body-parser: To parse incoming JSON requests.

Step 1.2: Organize the File Structure

Here’s the structure I used to keep everything neat and scalable:

login-signup-api/
|-- controllers/
|   |-- authController.js
|-- database/
|   |-- db.js
|-- middleware/
|   |-- authMiddleware.js
|-- routes/
|   |-- authRoutes.js
|-- .env
|-- app.js
|-- package.json
  • controllers/: Contains the logic for handling routes.
  • database/: Handles database connections.
  • middleware/: Custom middleware for authentication.
  • routes/: Defines the API routes.

2. Connecting to PostgreSQL

Before we dive into authentication, we need to set up the database.

Step 2.1: Install PostgreSQL (if not already installed)

Follow the instructions for your operating system to install PostgreSQL. Once installed, create a new database:

createdb login_signup_db

Step 2.2: Configure the Database Connection

Create a database/db.js file to manage the database connection:

import { Pool } from 'pg';
import dotenv from 'dotenv';

dotenv.config();

const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  password: process.env.DB_PASSWORD,
  port: process.env.DB_PORT,
});

export default pool;

Add your database credentials to a .env file:

DB_USER=your_username
DB_HOST=localhost
DB_NAME=login_signup_db
DB_PASSWORD=your_password
DB_PORT=5432

Step 2.3: Create the Users Table

Log in to the database and create a users table:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3. Implementing the API Endpoints

Step 3.1: Creating the Signup Endpoint

First, let’s handle new user registration in controllers/authController.js:

import bcrypt from 'bcrypt';
import pool from '../database/db.js';

export const signup = async (req, res) => {
  const { email, password } = req.body;

  try {
    // Check if the user already exists
    const userExists = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
    if (userExists.rows.length > 0) {
      return res.status(400).json({ message: 'User already exists.' });
    }

    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Insert the user into the database
    await pool.query('INSERT INTO users (email, password) VALUES ($1, $2)', [email, hashedPassword]);

    res.status(201).json({ message: 'User registered successfully!' });
  } catch (error) {
    res.status(500).json({ message: 'Server error.', error: error.message });
  }
};

Add the route in routes/authRoutes.js:

import express from 'express';
import { signup } from '../controllers/authController.js';

const router = express.Router();

router.post('/signup', signup);

export default router;

Wire the route in app.js:

import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import dotenv from 'dotenv';

import authRoutes from './routes/authRoutes.js';

dotenv.config();
const app = express();

app.use(cors());
app.use(bodyParser.json());
app.use('/api/auth', authRoutes);

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Step 3.2: Creating the Login Endpoint

Add the login logic in authController.js:

export const login = async (req, res) => {
  const { email, password } = req.body;

  try {
    // Check if the user exists
    const user = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
    if (user.rows.length === 0) {
      return res.status(404).json({ message: 'User not found.' });
    }

    // Compare passwords
    const isMatch = await bcrypt.compare(password, user.rows[0].password);
    if (!isMatch) {
      return res.status(401).json({ message: 'Invalid credentials.' });
    }

    res.status(200).json({ message: 'Login successful!' });
  } catch (error) {
    res.status(500).json({ message: 'Server error.', error: error.message });
  }
};

Add the login route in authRoutes.js:

import { login } from '../controllers/authController.js';

router.post('/login', login);

4. Testing the API

With everything set up, it’s time to test! Use a tool like Postman or Insomnia to send requests to the API:

Signup Request

  • Endpoint: POST /api/auth/signup
  • Body:
    {
      "email": "user@example.com",
      "password": "securepassword"
    }
    
  • Response:
    {
      "message": "User registered successfully!"
    }
    

Login Request

  • Endpoint: POST /api/auth/login
  • Body:
    {
      "email": "user@example.com",
      "password": "securepassword"
    }
    
  • Response:
    {
      "message": "Login successful!"
    }
    

5. Final Thoughts

This was both a challenge and a joy! Designing the backend for scalability with a clean structure of routes, controllers, and middleware made me appreciate the beauty of well-organized code. 


Also google is your greatest friend!!

If you’re building something similar, I’d love to hear about your process or help with any challenges you’re facing. Let’s grow together as developers!

#NodeJS #PostgreSQL #API #WebDevelopment #CodingJourney

Comments