Understanding the Problem
In this phase, we need to set up the backend of our authentication application. This includes creating the project structure, installing dependencies, setting up Sequelize, initializing Express, and configuring security middlewares.
The goal is to have a well-structured Express application ready for implementing authentication features in later phases.
Planning the Solution
- Create the project folder structure
- Set up the README.md file
- Create a .gitignore file
- Initialize Git repository
- Create backend and frontend folders
- Install necessary backend dependencies
- Set up environment variables
- Configure Sequelize
- Set up Express application with security middleware
- Create routes
- Configure the server
- Test the server
Implementing the Solution
1. Create Project Folder Structure
Start by creating a root folder for your project. The name should be meaningful and represent your project.
mkdir my_authentication_project
cd my_authentication_project
2. Set up README.md
Create a README.md file at the root of your project to document your API and database schema.
touch README.md
Add the following content to README.md:
# My Authentication Project
## Database Schema Design

## API Documentation
### All endpoints that require authentication
All endpoints that require a current user to be logged in.
* Request: endpoints that require authentication
* Error Response: Require authentication
* Status Code: 401
* Headers:
* Content-Type: application/json
* Body:
```json
{
"message": "Authentication required"
}
```
3. Create .gitignore File
Create a .gitignore file at the root to exclude certain files from version control:
touch .gitignore
Add the following content to .gitignore:
node_modules
.env
build
.DS_Store
*.db
4. Initialize Git Repository
Configure Git to use 'main' as the default branch, initialize the repository, and make your first commit:
git config --global init.defaultBranch main
git init
git add .
git commit -m 'Initial commit'
Create a remote GitHub repository and connect it to your local repository:
git remote add origin <github-remote-url>
git push origin main
5. Create Backend and Frontend Folders
Create folders to separate backend and frontend code:
mkdir backend frontend images
6. Install Backend Dependencies
Initialize the package.json file in the backend folder and install necessary dependencies:
cd backend
npm init -y
npm install cookie-parser cors csurf dotenv express express-async-errors helmet jsonwebtoken morgan per-env sequelize@6 sequelize-cli@6 pg
npm install -D sqlite3 dotenv-cli nodemon
7. Set Up Environment Variables
Create a .env file in the backend folder:
touch .env
Add the following content to the .env file:
PORT=8000
DB_FILE=db/dev.db
JWT_SECRET=your_secret_here
JWT_EXPIRES_IN=604800
SCHEMA=your_schema_name_here
To generate a strong JWT secret, you can use the openssl command:
openssl rand -base64 10
8. Create Configuration Files
Create a config folder in the backend folder and add an index.js file for environment variables:
mkdir -p backend/config
touch backend/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
}
};
9. Set Up Sequelize
Create a .sequelizerc file in the backend folder:
touch backend/.sequelizerc
Add the following content to .sequelizerc:
// backend/.sequelizerc
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')
};
Initialize Sequelize:
npx sequelize init
Update the config/database.js file with the following content:
// 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 psql-setup-script.js file in the backend folder:
touch backend/psql-setup-script.js
Add the following content to psql-setup-script.js:
// backend/psql-setup-script.js
const { sequelize } = require('./db/models');
sequelize.showAllSchemas({ logging: false }).then(async (data) => {
if (!data.includes(process.env.SCHEMA)) {
await sequelize.createSchema(process.env.SCHEMA);
}
});
Run a test migration to ensure Sequelize is set up correctly:
npx dotenv sequelize db:migrate
10. Set Up Express Application
Create an app.js file in the backend folder:
touch backend/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();
// Connect morgan middleware for logging
app.use(morgan('dev'));
// Parse cookies and JSON bodies
app.use(cookieParser());
app.use(express.json());
// Security middleware
if (!isProduction) {
// Enable CORS only in development
app.use(cors());
}
// Helmet helps set security headers
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
}
})
);
// Connect routes
const routes = require('./routes');
app.use(routes);
module.exports = app;
11. Create Routes
Set up the routes folder and create an index.js file:
mkdir -p backend/routes
touch backend/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;
12. Set Up Server
Create a bin folder and www file to start the server:
mkdir -p backend/bin
touch backend/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);
});
13. Configure Package.json Scripts
Update your backend/package.json file to include the following scripts:
"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"
}
14. Test the Server
Start the server and test the hello world route:
cd backend
npm start
Open your browser and navigate to http://localhost:8000/hello/world. You should see "Hello World!" displayed, and two cookies (_csrf and XSRF-TOKEN) should be set in your browser.
15. Add CSRF Token Restore Route
Update the routes/index.js file to include a route for restoring the CSRF token:
// backend/routes/index.js
// ...
// 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;
This route will be useful for frontend development to ensure the CSRF token is always available.
Review the Solution
At this point, you have successfully set up the backend of your authentication application. Let's review what you've accomplished:
- Created a well-structured project with separate backend and frontend folders
- Set up a Git repository with proper .gitignore
- Installed and configured necessary dependencies
- Set up environment variables for different environments
- Configured Sequelize for both development and production
- Created an Express application with security middleware
- Set up basic routes and CSRF protection
- Created a server that checks the database connection before starting
- Added a route to restore CSRF tokens
Your application is now ready for implementing API routes, which we'll cover in Phase 1.
Common Issues and Solutions
- Database Connection Errors: Make sure your database file path is correct in the .env file and you have the right permissions.
- CSRF Errors: When making non-GET requests, make sure to include the XSRF-TOKEN in the headers.
- Port Already in Use: If port 8000 is already in use, change the PORT in your .env file.
Next Steps
Now that you have set up the backend of your application, you are ready to move on to Phase 1: API Routes, where you will create API endpoints for your application.