In today’s digital landscape, security is paramount. Whether you’re developing a new project or enhancing an existing one, ensuring your web application is secure from the ground up is essential. In this guide, we’ll walk you through the process of quickly creating a secure Node.js-based web application with REST APIs.
You’ll learn how to set up Express.js for your backend, implement robust JWT authentication, and use bcrypt for password hashing to protect user data. We’ll also cover how to document your APIs effectively using Swagger and deploy your application on popular cloud platforms like AWS, Google Cloud, or Azure.
By the end of this blog, you’ll have a solid foundation for building a secure and scalable web application, armed with best practices in web development and API security. Let’s dive in and start building!
Set Up Your Development Environment
Make sure you have Node.js and npm installed. You can install them from Node.js official site.
Initialize Your Project
Create a new Node.js project and install necessary packages:
mkdir my-nodejs-app
cd my-nodejs-app
npm init -y
npm install express swagger-ui-express swagger-jsdoc jsonwebtoken bcryptjs cors
Set Up Express Server
Create an index.js
file to set up your Express server:
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const swaggerJsdoc = require('swagger-jsdoc');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const cors = require('cors');
const app = express();
app.use(express.json());
app.use(cors());
const swaggerOptions = {
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: 'Node.js API',
version: '1.0.0',
description: 'A simple Express API'
},
servers: [
{
url: 'http://localhost:3000'
}
]
},
apis: ['./routes/*.js']
};
const swaggerDocs = swaggerJsdoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
// Placeholder for authentication middleware and routes
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
Define Routes and Controllers
Create a routes
folder and inside it, create a user.js
file for user routes:
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
// Dummy user data
const users = [];
router.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
users.push({ username, password: hashedPassword });
res.status(201).send('User registered');
});
router.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).send('User not found');
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).send('Invalid credentials');
}
const token = jwt.sign({ username: user.username }, 'your_jwt_secret');
res.json({ token });
});
module.exports = router;
Update index.js
to include user routes:
const userRoutes = require('./routes/user');
app.use('/api/users', userRoutes);
Document Your API with Swagger
Add comments to your routes for Swagger documentation. Update user.js
:
/**
* @swagger
* components:
* schemas:
* User:
* type: object
* required:
* - username
* - password
* properties:
* username:
* type: string
* description: The user's username.
* password:
* type: string
* description: The user's password.
*/
/**
* @swagger
* /api/users/register:
* post:
* summary: Register a new user
* tags: [User]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* responses:
* 201:
* description: The user was successfully created
* 500:
* description: Some server error
*/
router.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
users.push({ username, password: hashedPassword });
res.status(201).send('User registered');
});
/**
* @swagger
* /api/users/login:
* post:
* summary: Log in a user
* tags: [User]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* responses:
* 200:
* description: User logged in successfully
* 400:
* description: Invalid credentials
*/
router.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).send('User not found');
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).send('Invalid credentials');
}
const token = jwt.sign({ username: user.username }, 'your_jwt_secret');
res.json({ token });
});
Secure Communication
Use HTTPS for secure communication. In a development environment, you can use tools like mkcert to create locally trusted development certificates.
Deploy on a Secure Cloud Platform
Deploy your application on a cloud platform like AWS, Google Cloud, or Azure. Use managed services for databases and other infrastructure components. Ensure you enable security features like firewalls, DDoS protection, and automated security updates.
Set Up CI/CD Pipeline
Use GitHub Actions to set up a CI/CD pipeline. Example .github/workflows/node.js.yml
:
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
- run: npm run build