Express and Sequelize Backend Setup

Understanding the Problem

We need to set up the backend of an application using Express and Sequelize. This involves several steps:

Creating a Plan

  1. Create project folder structure and README
  2. Set up Git repository
  3. Create backend and frontend separation
  4. Install necessary dependencies
  5. Configure environment variables
  6. Set up Sequelize configuration
  7. Create Express application with security middlewares
  8. Create initial routes
  9. Set up server startup script
  10. Test the server

Implementation

Step 1: Project Structure and README

First, create a project directory and set up the README with API documentation and database schema.

Expected output: A project folder with a README.md file containing API documentation and a database schema image.


# Create project directory
mkdir my_project
cd my_project

# Create README.md with API documentation from provided template
touch README.md

# Create images folder for schema
mkdir images
# Add database schema image to images folder
            

The README.md should follow this structure:


# [Your Project Name]

## Database Schema Design

![db-schema]

[db-schema]: ./images/your_schema_image.png

## API Documentation
# (API documentation content here)
            

Step 2: Git Setup

Set up Git for version control of your project.


# Create .gitignore file
echo "node_modules
.env
build
.DS_Store
*.db" > .gitignore

# Configure Git to use 'main' as default branch
git config --global init.defaultBranch main

# Initialize Git in project folder
git init

# Create GitHub repository and connect it
git remote add origin <github-remote-url>

# Make initial commit
git add .
git commit -m 'Initial commit'
git push origin main
            

Step 3: Backend and Frontend Separation

Create separate directories for backend and frontend code.


# Create backend and frontend folders
mkdir backend
mkdir frontend
            

Your file structure should now look like:


.
├── backend/
├── frontend/
├── images/
├── .gitignore
└── README.md
            

Step 4: Install Dependencies

Initialize package.json and install required dependencies in the backend folder.


# Navigate to backend folder
cd backend

# Initialize package.json
npm init -y

# Install production dependencies
npm install cookie-parser cors csurf dotenv express express-async-errors helmet jsonwebtoken morgan per-env sequelize@6 sequelize-cli@6 pg

# Install development dependencies
npm install -D sqlite3 dotenv-cli nodemon
            

Step 5: Configure Environment Variables

Create a .env file in the backend folder with required environment variables.


# Create .env file in backend folder
echo "PORT=8000
DB_FILE=db/dev.db
JWT_SECRET=$(openssl rand -base64 10)
JWT_EXPIRES_IN=604800
SCHEMA=your_schema_name" > .env
            

Then create a config file to read and export these variables:


# Create config folder and index.js
mkdir config
touch config/index.js
            

Add the following content to config/index.js:


// backend/config/index.js
module.exports = {
  environment: process.env.NODE_ENV || 'development',
  port: process.env.PORT || 8000,
  dbFile: process.env.DB_FILE,
  jwtConfig: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN
  }
};
            

Step 6: Sequelize Setup

Set up Sequelize configuration for database access.


# Create .sequelizerc file
echo "const path = require('path');

module.exports = {
  config: path.resolve('config', 'database.js'),
  'models-path': path.resolve('db', 'models'),
  'seeders-path': path.resolve('db', 'seeders'),
  'migrations-path': path.resolve('db', 'migrations')
};" > .sequelizerc

# Initialize Sequelize
npx sequelize init
            

Replace the content of config/database.js with:


// backend/config/database.js
const config = require('./index');

module.exports = {
  development: {
    storage: config.dbFile,
    dialect: "sqlite",
    seederStorage: "sequelize",
    logQueryParameters: true,
    typeValidation: true
  },
  production: {
    use_env_variable: 'DATABASE_URL',
    dialect: 'postgres',
    seederStorage: 'sequelize',
    dialectOptions: {
      ssl: {
        require: true,
        rejectUnauthorized: false
      }
    },
    define: {
      schema: process.env.SCHEMA
    }
  }
};
            

Create a PostgreSQL setup script:


# Create psql-setup-script.js
echo "const { sequelize } = require('./db/models');

sequelize.showAllSchemas({ logging: false }).then(async (data) => {
  if (!data.includes(process.env.SCHEMA)) {
    await sequelize.createSchema(process.env.SCHEMA);
  }
});" > psql-setup-script.js

# Test the database migration
npx dotenv sequelize db:migrate
            

Step 7: Express Application Setup

Create the Express application with security middlewares.


# Create app.js file
touch app.js
            

Add the following content to app.js:


// backend/app.js
const express = require('express');
require('express-async-errors');
const morgan = require('morgan');
const cors = require('cors');
const csurf = require('csurf');
const helmet = require('helmet');
const cookieParser = require('cookie-parser');

const { environment } = require('./config');
const isProduction = environment === 'production';

const app = express();

app.use(morgan('dev'));
app.use(cookieParser());
app.use(express.json());

// Security Middleware
if (!isProduction) {
  // enable cors only in development
  app.use(cors());
}

// helmet helps set a variety of headers to better secure your app
app.use(
  helmet.crossOriginResourcePolicy({
    policy: "cross-origin"
  })
);

// Set the _csrf token and create req.csrfToken method
app.use(
  csurf({
    cookie: {
      secure: isProduction,
      sameSite: isProduction && "Lax",
      httpOnly: true
    }
  })
);

const routes = require('./routes');
app.use(routes);

module.exports = app;
            

Step 8: Create Routes

Set up the initial routes for the application.


# Create routes folder and index.js
mkdir routes
touch routes/index.js
            

Add the following content to routes/index.js:


// backend/routes/index.js
const express = require('express');
const router = express.Router();

router.get('/hello/world', function(req, res) {
  res.cookie('XSRF-TOKEN', req.csrfToken());
  res.send('Hello World!');
});

module.exports = router;
            

Step 9: Server Startup Script

Create the server startup script in bin/www.


# Create bin folder and www file
mkdir bin
touch bin/www
            

Add the following content to bin/www:


#!/usr/bin/env node
// backend/bin/www

// Import environment variables
require('dotenv').config();

const { port } = require('../config');

const app = require('../app');
const db = require('../db/models');

// Check the database connection before starting the app
db.sequelize
  .authenticate()
  .then(() => {
    console.log('Database connection success! Sequelize is ready to use...');

    // Start listening for connections
    app.listen(port, () => console.log(`Listening on port ${port}...`));
  })
  .catch((err) => {
    console.log('Database connection failure.');
    console.error(err);
  });
            

Step 10: Update package.json Scripts

Add scripts to package.json for running the server.


// Update the scripts section in package.json
"scripts": {
  "sequelize": "sequelize",
  "sequelize-cli": "sequelize-cli",
  "start": "per-env",
  "start:development": "nodemon ./bin/www",
  "start:production": "node ./bin/www",
  "build": "node psql-setup-script.js"
}
            

Step 11: Test the Server

Start the server and test the routes.


# Start the server
npm start

# Test the route by navigating to http://localhost:8000/hello/world
            

Step 12: Add CSRF Token Restore Route

Add a route to restore the CSRF token.

Update routes/index.js with:


// backend/routes/index.js
const express = require('express');
const router = express.Router();

// Remove the test route
// router.get('/hello/world', function(req, res) {
//   res.cookie('XSRF-TOKEN', req.csrfToken());
//   res.send('Hello World!');
// });

// Add a XSRF-TOKEN cookie
router.get("/api/csrf/restore", (req, res) => {
  const csrfToken = req.csrfToken();
  res.cookie("XSRF-TOKEN", csrfToken);
  res.status(200).json({
    'XSRF-Token': csrfToken
  });
});

module.exports = router;
            

Testing and Verification

Now that we've completed the backend setup, let's verify everything is working properly:

  1. Start your server with npm start in the backend directory
  2. Navigate to http://localhost:8000/hello/world to test the initial route
  3. Check that the CSRF cookies are being set in your browser's Developer Tools
  4. Once confirmed, remove the test route and add the CSRF restore route
  5. Test the CSRF restore route at http://localhost:8000/api/csrf/restore
  6. Commit your code with a message like "Initialize Express and Sequelize with CSRF protection"

Common Issues and Troubleshooting

Real-World Application

This backend setup forms the foundation for a modern web application following the client-server architecture. The Express server provides the API endpoints that will be consumed by a React frontend. This separation of concerns allows for:

Many popular applications like Twitter, Facebook, and Instagram use similar architecture, with a robust backend API serving data to a dynamic frontend client.