DEV Community

Cover image for The Ultimate Node.js TypeScript Project Setup: DevOps Essentials Included
Omor Faruk
Omor Faruk

Posted on • Edited on

The Ultimate Node.js TypeScript Project Setup: DevOps Essentials Included

A Comprehensive Overview of a Node.js TypeScript Project with DevOps: Structure, Code, and Configuration

Explore the essential components of building a robust Node.js TypeScript project integrated with DevOps practices. This guide provides a detailed breakdown of the project structure, complete with code snippets and configurations for each file and directory, empowering you to streamline development, testing, and deployment processes effectively. Whether you're a beginner or an experienced developer, this comprehensive overview will enhance your understanding of creating scalable applications in a modern development environment.

Project Structure

πŸ“ node-typescript-project-with-devops/ β”œβ”€β”€ πŸ“ node_modules # Node.js modules β”œβ”€β”€ πŸ“„ .gitignore # Specifies files and directories to be ignored by Git β”œβ”€β”€ πŸ“„ .dockerignore # Specifies files and directories to be ignored by Docker β”œβ”€β”€ πŸ“„ .env # Environment variables file β”œβ”€β”€ πŸ“„ example.env # Example environment variables file β”œβ”€β”€ πŸ“ uploads # Folder for uploaded files β”‚ └── ... β”œβ”€β”€ πŸ“ src β”‚ β”œβ”€β”€ πŸ“„ server.ts # Main entry point for the application β”‚ β”œβ”€β”€ πŸ“ bin # Contains binaries or executable files β”‚ β”‚ └── πŸ“„ www # Start script for the server β”‚ β”œβ”€β”€ πŸ“ config # Configuration files β”‚ β”‚ β”œβ”€β”€ πŸ“„ redis.config # Redis configuration β”‚ β”‚ β”œβ”€β”€ πŸ“„ mongo.config # MongoDB configuration β”‚ β”‚ β”œβ”€β”€ πŸ“„ postgres.config # PostgreSQL configuration β”‚ β”‚ └── πŸ“„ nginx.conf # Nginx configuration file β”‚ β”œβ”€β”€ πŸ“ controllers # Controller files β”‚ β”‚ β”œβ”€β”€ πŸ“„ customer.ts # Customer-related controller β”‚ β”‚ β”œβ”€β”€ πŸ“„ product.ts # Product-related controller β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ πŸ“ middleware # Middleware files β”‚ β”‚ β”œβ”€β”€ πŸ“„ auth.ts # Authentication middleware β”‚ β”‚ β”œβ”€β”€ πŸ“„ logger.ts # Logger middleware β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ πŸ“ models # Model files β”‚ β”‚ β”œβ”€β”€ πŸ“„ customer.ts # Customer model β”‚ β”‚ β”œβ”€β”€ πŸ“„ product.ts # Product model β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ πŸ“ routes # Route files β”‚ β”‚ β”œβ”€β”€ πŸ“„ api.ts # API routes β”‚ β”‚ β”œβ”€β”€ πŸ“„ auth.ts # Authentication routes β”‚ β”‚ └── ... β”‚ β”œβ”€β”€ πŸ“ tests # Test files β”‚ β”‚ β”œβ”€β”€ πŸ“ unit # Unit tests β”‚ β”‚ β”œβ”€β”€ πŸ“ integration # Integration tests β”‚ β”‚ β”œβ”€β”€ πŸ“ e2e # End-to-end tests β”‚ β”‚ └── ... β”‚ └── πŸ“ utils # Utility files β”‚ β”œβ”€β”€ πŸ“„ validation.ts # Validation utilities β”‚ β”œβ”€β”€ πŸ“„ helpers.ts # Helper functions β”‚ └── ... β”œβ”€β”€ πŸ“„ Dockerfile # Dockerfile for building the Node.js app image β”œβ”€β”€ πŸ“„ docker-compose.yml # Docker Compose configuration β”œβ”€β”€ πŸ“„ nodemon.json # Nodemon configuration for development β”œβ”€β”€ πŸ“„ package.json # Project dependencies and scripts β”œβ”€β”€ πŸ“ ansible # Ansible directory β”‚ β”œβ”€β”€ πŸ“„ deploy.yml # Ansible playbook for deployment β”‚ └── πŸ“ roles # Ansible roles (if any) β”‚ └── ... └── πŸ“ helm # Helm chart directory β”œβ”€β”€ πŸ“ your-helm-chart/ β”‚ β”œβ”€β”€ πŸ“ charts # Charts for dependencies β”‚ β”œβ”€β”€ πŸ“ templates # Template files for Kubernetes resources β”‚ β”‚ β”œβ”€β”€ πŸ“„ deployment.yaml # Deployment configuration for Node.js app β”‚ β”‚ β”œβ”€β”€ πŸ“„ service.yaml # Service configuration for exposing the Node.js app β”‚ β”‚ β”œβ”€β”€ πŸ“„ ingress.yaml # Ingress configuration for Nginx β”‚ β”‚ └── πŸ“„ configmap.yaml # ConfigMap for environment variables β”‚ β”œβ”€β”€ πŸ“„ Chart.yaml # Metadata for your Helm chart β”‚ └── πŸ“„ values.yaml # Default values for your templates β”œβ”€β”€ πŸ“ jenkins # Jenkins directory for pipeline configuration β”‚ β”œβ”€β”€ πŸ“„ Jenkinsfile # Declarative pipeline for Jenkins β”‚ β”œβ”€β”€ πŸ“„ jenkins-config.groovy # Configuration script for Jenkins setup β”‚ └── πŸ“ jobs # Jenkins job configuration files β”‚ β”œβ”€β”€ πŸ“„ build-node-app.xml # Jenkins job for building Node.js app β”‚ └── πŸ“„ deploy-node-app.xml # Jenkins job for deploying Node.js app 
Enter fullscreen mode Exit fullscreen mode

Detailed File and Code Examples

1. .gitignore

node_modules/ dist/ .env npm-debug.log .DS_Store uploads/ 
Enter fullscreen mode Exit fullscreen mode

2. .dockerignore

node_modules npm-debug.log .git .gitignore Dockerfile docker-compose.yml 
Enter fullscreen mode Exit fullscreen mode

3. .env

NODE_ENV=production PORT=3000 MONGO_URI=mongodb://localhost:27017/mydatabase REDIS_URL=redis://localhost:6379 POSTGRES_URI=postgresql://user:password@localhost:5432/mydatabase JWT_SECRET=mysecret 
Enter fullscreen mode Exit fullscreen mode

4. example.env

NODE_ENV=development PORT=3000 MONGO_URI=mongodb://localhost:27017/mydatabase REDIS_URL=redis://localhost:6379 POSTGRES_URI=postgresql://user:password@localhost:5432/mydatabase JWT_SECRET=mysecret 
Enter fullscreen mode Exit fullscreen mode

5. src/server.ts

import express from 'express'; import mongoose from 'mongoose'; import { json } from 'body-parser'; import { routes } from './routes/api'; import { connectRedis } from './config/redis.config'; import { connectMongo } from './config/mongo.config'; const app = express(); const PORT = process.env.PORT || 3000; app.use(json()); app.use('/api', routes); // MongoDB and Redis connection connectMongo(); connectRedis(); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); 
Enter fullscreen mode Exit fullscreen mode

6. src/bin/www

#!/usr/bin/env node  import app from '../server'; const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server started on port ${PORT}`); }); 
Enter fullscreen mode Exit fullscreen mode

7. src/config/redis.config

import Redis from 'ioredis'; const redis = new Redis(process.env.REDIS_URL); export const connectRedis = () => { redis.on('connect', () => { console.log('Connected to Redis'); }); redis.on('error', (err) => { console.error('Redis error:', err); }); }; 
Enter fullscreen mode Exit fullscreen mode

8. src/config/mongo.config

import mongoose from 'mongoose'; export const connectMongo = async () => { try { await mongoose.connect(process.env.MONGO_URI); console.log('Connected to MongoDB'); } catch (error) { console.error('MongoDB connection error:', error); } }; 
Enter fullscreen mode Exit fullscreen mode

9. src/config/postgres.config

// PostgreSQL configuration // You can set up Sequelize or pg-promise here 
Enter fullscreen mode Exit fullscreen mode

10. src/config/nginx.conf

server { listen 80; location / { proxy_pass http://localhost:3000; # Adjust according to your Node.js server proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } } 
Enter fullscreen mode Exit fullscreen mode

11. src/controllers/customer.ts

import { Request, Response } from 'express'; export const getCustomers = (req: Request, res: Response) => { // Logic to get customers }; export const createCustomer = (req: Request, res: Response) => { // Logic to create a customer }; 
Enter fullscreen mode Exit fullscreen mode

12. src/controllers/product.ts

import { Request, Response } from 'express'; export const getProducts = (req: Request, res: Response) => { // Logic to get products }; export const createProduct = (req: Request, res: Response) => { // Logic to create a product }; 
Enter fullscreen mode Exit fullscreen mode

13. src/middleware/auth.ts

import { Request, Response, NextFunction } from 'express'; export const authMiddleware = (req: Request, res: Response, next: NextFunction) => { // Authentication logic next(); }; 
Enter fullscreen mode Exit fullscreen mode

14. src/middleware/logger.ts

import { Request, Response, NextFunction } from 'express'; export const loggerMiddleware = (req: Request, res: Response, next: NextFunction) => { console.log(`${req.method} ${req.url}`); next(); }; 
Enter fullscreen mode Exit fullscreen mode

15. src/models/customer.ts

import mongoose, { Schema, Document } from 'mongoose'; export interface ICustomer extends Document { name: string; email: string; } const CustomerSchema: Schema = new Schema({ name: { type: String, required: true }, email: { type: String, required: true }, }); export const Customer = mongoose .model<ICustomer>('Customer', CustomerSchema); 
Enter fullscreen mode Exit fullscreen mode

16. src/models/product.ts

import mongoose, { Schema, Document } from 'mongoose'; export interface IProduct extends Document { title: string; price: number; } const ProductSchema: Schema = new Schema({ title: { type: String, required: true }, price: { type: Number, required: true }, }); export const Product = mongoose.model<IProduct>('Product', ProductSchema); 
Enter fullscreen mode Exit fullscreen mode

17. src/routes/api.ts

import { Router } from 'express'; import { getCustomers, createCustomer } from '../controllers/customer'; import { getProducts, createProduct } from '../controllers/product'; const router = Router(); router.get('/customers', getCustomers); router.post('/customers', createCustomer); router.get('/products', getProducts); router.post('/products', createProduct); export const routes = router; 
Enter fullscreen mode Exit fullscreen mode

18. src/tests/unit/customer.test.ts

import { getCustomers } from '../../controllers/customer'; import { Request, Response } from 'express'; describe('Customer Controller', () => { it('should get customers', () => { const req = {} as Request; const res = { send: jest.fn(), } as unknown as Response; getCustomers(req, res); expect(res.send).toHaveBeenCalled(); }); }); 
Enter fullscreen mode Exit fullscreen mode

19. Dockerfile

# Use an official Node.js runtime as a parent image FROM node:14 # Set the working directory WORKDIR /usr/src/app # Copy package.json and package-lock.json COPY package*.json ./ # Install dependencies RUN npm install # Copy the rest of the application code COPY . . # Expose the application port EXPOSE 3000 # Command to run the application CMD [ "npm", "start" ] 
Enter fullscreen mode Exit fullscreen mode

20. docker-compose.yml

version: '3.8' services: node-app: build: . ports: - '3000:3000' environment: NODE_ENV: development MONGO_URI: mongodb://mongo:27017/mydatabase REDIS_URL: redis://redis:6379 POSTGRES_URI: postgresql://user:password@postgres:5432/mydatabase depends_on: - mongo - redis - postgres mongo: image: mongo restart: always ports: - '27017:27017' redis: image: redis restart: always ports: - '6379:6379' postgres: image: postgres restart: always environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydatabase ports: - '5432:5432' 
Enter fullscreen mode Exit fullscreen mode

21. nodemon.json

{ "watch": ["src"], "ext": "ts", "exec": "ts-node src/server.ts" } 
Enter fullscreen mode Exit fullscreen mode

22. package.json

{ "name": "node-typescript-project-with-devops", "version": "1.0.0", "scripts": { "start": "ts-node src/server.ts", "dev": "nodemon", "test": "jest" }, "dependencies": { "express": "^4.17.1", "mongoose": "^5.10.9", "ioredis": "^4.17.3", "dotenv": "^8.2.0", "pg": "^8.5.1" }, "devDependencies": { "@types/express": "^4.17.11", "@types/jest": "^26.0.0", "jest": "^26.6.0", "ts-jest": "^26.4.0", "typescript": "^4.0.3", "nodemon": "^2.0.4", "ts-node": "^9.1.1" } } 
Enter fullscreen mode Exit fullscreen mode

23. ansible/deploy.yml

--- - hosts: all tasks: - name: Ensure Node.js is installed apt: name: nodejs state: present - name: Copy application files copy: src: /path/to/your/local/project/ dest: /path/to/remote/server/ - name: Install dependencies command: npm install args: chdir: /path/to/remote/server/ 
Enter fullscreen mode Exit fullscreen mode

24. helm/your-helm-chart/templates/deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: node-app spec: replicas: 2 selector: matchLabels: app: node-app template: metadata: labels: app: node-app spec: containers: - name: node-app image: your-node-app-image ports: - containerPort: 3000 env: - name: NODE_ENV value: "production" - name: MONGO_URI value: "mongodb://mongo:27017/mydatabase" - name: REDIS_URL value: "redis://redis:6379" - name: POSTGRES_URI value: "postgresql://user:password@postgres:5432/mydatabase" 
Enter fullscreen mode Exit fullscreen mode

25. helm/your-helm-chart/templates/service.yaml

apiVersion: v1 kind: Service metadata: name: node-app spec: type: LoadBalancer ports: - port: 80 targetPort: 3000 selector: app: node-app 
Enter fullscreen mode Exit fullscreen mode

26. jenkins/Jenkinsfile

pipeline { agent any stages { stage('Build') { steps { script { docker.build("your-node-app-image") } } } stage('Deploy') { steps { script { sh 'docker-compose up -d' } } } } } 
Enter fullscreen mode Exit fullscreen mode

27. jenkins/jenkins-config.groovy

// Jenkins configuration script for initial setup 
Enter fullscreen mode Exit fullscreen mode

Additional Configuration and Usage

  1. Run the Application:

    • Use npm start or npm run dev to start the application in development mode with Nodemon.
  2. Docker:

    • Build the Docker image with docker build -t your-node-app-image .
    • Run docker-compose up to start all services defined in docker-compose.yml.
  3. Ansible:

    • Execute the Ansible playbook with ansible-playbook deploy.yml.
  4. Helm:

    • Package and deploy your Helm chart using helm install your-helm-chart.
  5. Jenkins:

    • Set up your Jenkins server and create a pipeline using the Jenkinsfile provided.
  6. Testing:

    • Run tests using npm test.

This structure and configuration allow for a solid foundation for a Node.js TypeScript project with DevOps best practices, making it easier to develop, test, and deploy your application efficiently. If you have any specific requirements or changes, feel free to change this structure is't Bible :)

Happy coding...

Top comments (0)