Sequelize with PostgreSQL in Node.js: Part 1 – Setup and Configuration

Sequelize with PostgreSQL in Node.js: Part 1 – Setup and Configuration

Building a robust backend requires more than just writing code. It requires a solid foundation where your application can seamlessly communicate with your database. In this comprehensive series, we’ll explore how to integrate Sequelize, a powerful ORM (Object-Relational Mapping) library, with PostgreSQL in a Node.js environment. This first part focuses on setting up your project from scratch and establishing that critical database connection.

What is Sequelize and Why Use It?

Sequelize is a promise-based ORM for Node.js that simplifies database interactions. Instead of writing raw SQL queries, you work with JavaScript objects and models. This abstraction layer brings several benefits: database independence (you can switch databases with minimal code changes), built-in validation, migration support, and powerful querying capabilities. When combined with PostgreSQL, you get a reliable, scalable, and production-ready stack.

Prerequisites

Before diving in, ensure you have the following installed on your system:

  • Node.js (v18 or higher recommended)
  • PostgreSQL (v13 or higher) or Docker for running PostgreSQL in a container
  • npm or yarn package manager
  • A code editor (VS Code recommended)
  • Basic knowledge of JavaScript and async/await

Step 1: Project Initialization

Start by creating a new directory for your project and initializing it:

mkdir sequelize-postgres-app
cd sequelize-postgres-app
npm init -y

This creates a package.json file with default settings. Your project structure will eventually look like this:

sequelize-postgres-app/
├── src/
│   ├── config/
│   ├── models/
│   ├── migrations/
│   ├── seeders/
│   └── index.js
├── .env
├── .sequelizerc
├── package.json
└── docker-compose.yml (optional)

Step 2: Installing Dependencies

Install the core packages needed for your application:

npm install express sequelize pg pg-hstore dotenv cors helmet
npm install --save-dev nodemon sequelize-cli

Here’s what each package does:

  • express: Web framework for Node.js
  • sequelize: ORM for database operations
  • pg: PostgreSQL client driver
  • pg-hstore: Used to serialize and deserialize JSON data in Postgres
  • dotenv: Manages environment variables
  • cors: Enable Cross-Origin Resource Sharing
  • helmet: Secure HTTP headers
  • nodemon: Auto-restart server during development
  • sequelize-cli: Command-line interface for Sequelize operations

Step 3: Setting Up PostgreSQL with Docker (Optional but Recommended)

If you don’t have PostgreSQL installed locally, Docker provides a clean solution. Create a docker-compose.yml file in your project root:

version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    container_name: sequelize_postgres_db
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres_password
      POSTGRES_DB: sequelize_dev_db
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - sequelize_network

networks:
  sequelize_network:
    driver: bridge

volumes:
  postgres_data:

Start the PostgreSQL container with:

docker-compose up -d

Step 4: Environment Configuration

Create a .env file in your project root to store sensitive information:

NODE_ENV=development
PORT=3000

DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres_password
DB_NAME=sequelize_dev_db

Add .env to your .gitignore to prevent exposing secrets:

echo ".env" >> .gitignore

Step 5: Initialize Sequelize

Initialize Sequelize in your project:

npx sequelize-cli init

This creates config, models, migrations, and seeders directories. However, we’ll customize the structure. Create a .sequelizerc file in your project root to define custom paths:

const path = require('path');

module.exports = {
  config: path.resolve('./src/config', 'database.js'),
  'models-path': path.resolve('./src/models'),
  'seeders-path': path.resolve('./src/seeders'),
  'migrations-path': path.resolve('./src/migrations'),
};

Step 6: Database Configuration

Create src/config/database.js to manage database connections:

require('dotenv').config();

module.exports = {
  development: {
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    dialect: 'postgres',
    logging: console.log,
    pool: {
      max: 5,
      min: 0,
      acquire: 30000,
      idle: 10000,
    },
  },
  production: {
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    dialect: 'postgres',
    logging: false,
    pool: {
      max: 10,
      min: 2,
      acquire: 30000,
      idle: 10000,
    },
  },
};

The pool configuration manages connection pooling. In development, we log queries for debugging. In production, we disable logging and increase pool size for better performance.

Step 7: Creating the Database Connection

Create src/config/sequelize.js to establish and manage the Sequelize connection:

const { Sequelize } = require('sequelize');
const config = require('./database');

const env = process.env.NODE_ENV || 'development';
const dbConfig = config[env];

const sequelize = new Sequelize(
  dbConfig.database,
  dbConfig.username,
  dbConfig.password,
  {
    host: dbConfig.host,
    port: dbConfig.port,
    dialect: dbConfig.dialect,
    logging: dbConfig.logging,
    pool: dbConfig.pool,
  }
);

sequelize.authenticate()
  .then(() => {
    console.log('Database connection established successfully');
  })
  .catch((error) => {
    console.error('Failed to connect to database:', error.message);
    process.exit(1);
  });

module.exports = sequelize;

Step 8: Creating the Entry Point

Create src/index.js as your application entry point:

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const sequelize = require('./config/sequelize');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(helmet());
app.use(cors());
app.use(express.json());

app.get('/health', (req, res) => {
  res.json({ status: 'Server is running' });
});

const startServer = async () => {
  try {
    await sequelize.authenticate();
    await sequelize.sync({ alter: false });
    
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });
  } catch (error) {
    console.error('Failed to start server:', error);
    process.exit(1);
  }
};

startServer();

Step 9: Update package.json Scripts

Update the scripts section in your package.json to add useful commands:

"scripts": {
  "dev": "nodemon src/index.js",
  "start": "node src/index.js",
  "db:migrate": "sequelize-cli db:migrate",
  "db:migrate:undo": "sequelize-cli db:migrate:undo",
  "db:seed": "sequelize-cli db:seed:all",
  "db:seed:undo": "sequelize-cli db:seed:undo:all"
},

Step 10: Test Your Connection

Run your application in development mode:

npm run dev

You should see output similar to:

Database connection established successfully
Server running on port 3000

Test the health endpoint:

curl http://localhost:3000/health

Architecture Visualization

graph TB
    subgraph App["Node.js Application"]
        Express["Express Server"]
        Sequelize["Sequelize ORM"]
        Models["Data Models"]
    end
    
    subgraph DB["PostgreSQL Database"]
        Tables["Tables & Schema"]
        Data["Data Storage"]
    end
    
    Express -->|Routes & Middleware| Sequelize
    Sequelize -->|Queries & Transactions| Models
    Models -->|SQL Operations| Tables
    Tables -->|Persist| Data
    
    style App fill:#2d2d2d,stroke:#61dafb,color:#fff
    style DB fill:#336791,stroke:#fff,color:#fff
    style Express fill:#90c53f,stroke:#fff,color:#000
    style Sequelize fill:#0ea5e9,stroke:#fff,color:#fff

Key Takeaways

  • Sequelize abstracts SQL queries into JavaScript objects, improving code readability and maintainability
  • Proper environment configuration keeps sensitive data secure
  • Connection pooling optimizes database resource usage
  • Docker simplifies PostgreSQL setup without local installation
  • The .sequelizerc file allows customization of directory structure for better organization

Common Issues and Solutions

Issue: “ECONNREFUSED 127.0.0.1:5432”
Solution: Ensure PostgreSQL is running. If using Docker, run docker-compose up -d and verify the container is active with docker ps.

Issue: “Module not found” errors
Solution: Run npm install again to ensure all dependencies are installed.

Issue: Environment variables not loading
Solution: Ensure .env file is in the project root and you’re using require(‘dotenv’).config() at the top of your files.

What’s Next

In Part 2, we’ll dive deep into creating data models, understanding migrations, and establishing relationships between tables. We’ll build a real-world example with User and Post models, exploring one-to-many and many-to-many relationships. Stay tuned!

References

  • Sequelize Official Documentation: https://sequelize.org/
  • PostgreSQL Official Website: https://www.postgresql.org/
  • Node.js Best Practices: https://nodejs.org/en/docs/guides/
  • Express.js Guide: https://expressjs.com/
  • Docker Documentation: https://docs.docker.com/

Written by:

396 Posts

View All Posts
Follow Me :