Understanding the Problem
We need to set up the backend of an application using Express and Sequelize. This involves several steps:
- Setting up project structure and initial files
- Installing dependencies
- Configuring Sequelize
- Setting up Express application with security middleware
- Creating initial routes
- Testing the server setup
Creating a Plan
- Create project folder structure and README
- Set up Git repository
- Create backend and frontend separation
- Install necessary dependencies
- Configure environment variables
- Set up Sequelize configuration
- Create Express application with security middlewares
- Create initial routes
- Set up server startup script
- 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:
- Start your server with
npm startin the backend directory - Navigate to
http://localhost:8000/hello/worldto test the initial route - Check that the CSRF cookies are being set in your browser's Developer Tools
- Once confirmed, remove the test route and add the CSRF restore route
- Test the CSRF restore route at
http://localhost:8000/api/csrf/restore - Commit your code with a message like "Initialize Express and Sequelize with CSRF protection"
Common Issues and Troubleshooting
- Database Connection Issues: Check that your database configuration is correct and that the database file exists in the specified location.
- Port Already in Use: If port 8000 is already in use, change the PORT in your .env file.
- CSRF Token Issues: Make sure you're including the CSRF token in all non-GET requests to the API.
- Module Not Found Errors: Ensure all dependencies are installed correctly with
npm install.
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:
- Scalability: The backend and frontend can scale independently based on demand.
- Security: With properly implemented CSRF protection, CORS, and other security middleware, your application is better protected against common web vulnerabilities.
- Maintainability: Clear separation between backend and frontend makes the codebase easier to maintain and update.
Many popular applications like Twitter, Facebook, and Instagram use similar architecture, with a robust backend API serving data to a dynamic frontend client.