DEV Community

Cover image for Enterprise-Grade Node.js: Leveraging TypeScript, ESLint & Prettier for Production Excellence
Yug Jadvani
Yug Jadvani

Posted on

Enterprise-Grade Node.js: Leveraging TypeScript, ESLint & Prettier for Production Excellence

In today's rapidly evolving digital landscape, the quality and maintainability of code can determine competitive advantage. Our discussion today outlines a production-ready Node.js setup that integrates TypeScript, ESLint, and Prettier. This approach not only enforces coding standards but also minimizes technical debt, thereby accelerating delivery cycles and boosting overall reliability a key differentiator for top companies.


Project Initialization & Tooling Strategy

Why this setup matters:

Traditional Node.js projects often evolve ad hoc, leading to inconsistent code quality and unforeseen bugs. By standardizing the development environment with TypeScript for type safety, ESLint for static code analysis, and Prettier for automated formatting, organizations can secure a robust foundation. This architecture is particularly critical when scaling applications or transitioning legacy codebases into a modern ecosystem.

Project Bootstrapping

A well-initialized project sets the stage for efficiency. Start by creating your project directory and initializing with npm:

mkdir your-project
cd your-project
npm init -y
Enter fullscreen mode Exit fullscreen mode

These commands establish a reproducible starting point, ensuring that all future team members work from the same baseline.

Integrate TypeScript

TypeScript adds a layer of type safety and predictability to JavaScript. Installing it as a development dependency and generating an initial configuration are key steps:

npm install --save-dev typescript @types/node
npx tsc --init
Enter fullscreen mode Exit fullscreen mode

The generated tsconfig.json allows customization of the TypeScript compiler options, ensuring the codebase adheres to stringent type-checking rules a critical factor for long-term maintenance and scalable development.


Structured Code & Dependency Management

A clear directory structure and proper dependency management ensure seamless collaboration across teams. Here's an example folder hierarchy that supports a large-scale application:

folder-structure/
├─ .env.sample
├─ .eslintignore
├─ .eslintrc.json
├─ .gitignore
├─ .prettierignore
├─ .prettierrc
├─ public/
│  └─ temp/
│     └─ .gitkeep
├─ src/
│  ├─ app.ts
│  ├─ constants.ts
│  ├─ controllers/
│  │  └─ .gitkeep
│  ├─ db/
│  │  └─ db.ts
│  ├─ index.ts
│  ├─ middlewares/
│  │  └─ .gitkeep
│  ├─ models/
│  │  └─ .gitkeep
│  ├─ routes/
│  │  └─ .gitkeep
│  └─ utils/
│     └─ .gitkeep
└─ tsconfig.json
Enter fullscreen mode Exit fullscreen mode

The design follows best practices in modularity and separation of concerns ensuring that API routes, business logic, and utility functions are clearly delineated. Such an architecture improves code readability and eases onboarding of new team members.

Updating NPM Scripts

Optimized scripts accelerate the development lifecycle. Update your package.json with the following scripts:

"scripts": {
  "dev": "nodemon --exec ts-node src/index.ts",
  "start": "ts-node src/index.ts",
  "lint": "eslint src/**/*.{ts,tsx}",
  "prettier": "prettier --write src/**/*.{ts,tsx}",
  "format": "npm run prettier && npm run lint",
  "test": "echo \"Error: no test specified\" && exit 1"
},
Enter fullscreen mode Exit fullscreen mode

This integrated script suite ensures that code formatting and linting are enforced consistently across development, reducing human error and maintaining a production-grade code quality.

Installation of Core Dependencies

For robust backend functionality, install the following dependencies:

npm install bcrypt cors crypto dotenv express jsonwebtoken multer node-cron nodemailer pg
Enter fullscreen mode Exit fullscreen mode

And for development dependencies, install:

npm install --save-dev @eslint/js @types/bcrypt @types/cors @types/express @types/jsonwebtoken @types/multer @types/node-cron @types/nodemailer @types/pg @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier eslint-plugin-prettier globals nodemon prettier ts-node typescript typescript-eslint
Enter fullscreen mode Exit fullscreen mode

These dependencies have been vetted to ensure stability, security, and performance in a production environment. Their integration is fundamental to maintaining high standards in code consistency and application reliability.


Advanced Configuration: ESLint & Prettier

Prettier Configuration

Prettier enforces a consistent code style, which is essential when scaling teams. Below is a sample configuration (.prettierrc):

{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "bracketSpacing": true,
  "jsxBracketSameLine": true,
  "arrowParens": "always",
  "proseWrap": "preserve",
  "endOfLine": "auto"
}
Enter fullscreen mode Exit fullscreen mode

And the corresponding ignore file (.prettierignore):

node_modules/
coverage/
build/
dist/
*.min.js
*.min.css
*.js.map
*.css.map
*.json
*.md
*.yml
*.yaml
*.txt
*.lock
*.gitignore
.DS_Store
Enter fullscreen mode Exit fullscreen mode

ESLint Configuration

ESLint coupled with Prettier ensures that the code not only follows stylistic guidelines but also adheres to best coding practices. An advanced ESLint configuration (eslint.config.mjs) might look like this:

import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintConfigPrettier from 'eslint-config-prettier';
import prettier from 'eslint-plugin-prettier';

/** @type {import('eslint').Linter.Config[]} */
export default [
  {
    files: ["**/*.{js,mjs,cjs,ts}"],
    languageOptions: {
      globals: {
        ...globals.browser
      }
    },
    plugins: {
      prettier: prettier
    },
    rules: {
      "prettier/prettier": "error",
      "semi": ["warn", "always"]
    },
    ignores: ['node_modules/', 'public/']
  },
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  eslintConfigPrettier
];
Enter fullscreen mode Exit fullscreen mode

This configuration integrates global variables and leverages recommended settings from ESLint and TypeScript ESLint plugins. It also disables conflicting Prettier rules to streamline the development process.


Application & API Response Standardization

Express Application Setup

The core of your application starts with setting up Express in app.ts:

/**
 * Express Application Configuration
 * Configures middleware for CORS, body parsing, and static file serving.
 */
import express from 'express';
import cors from 'cors';
import routes from './routes';

const app = express();

app.use(
  cors({
    origin: process.env.CORS_ORIGIN,
    credentials: true,
  }),
);
app.use(express.json({ limit: '16kb' }));
app.use(express.urlencoded({ extended: true, limit: '16kb' }));
app.use(express.static('public'));

// Mount API routes under /api/v1 prefix
app.use('/api/v1', routes);

export default app;
Enter fullscreen mode Exit fullscreen mode

The middleware configuration ensures that your API remains secure, scalable, and capable of handling a high throughput of requests a necessity in production-grade applications.

Server Entry Point

The server is initiated via index.ts:

/**
 * Server Entry Point
 * Starts the Express server on a specified port.
 */
import app from './app';

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
  console.log(`🚀 Server is running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

A clear separation between application logic and server bootstrapping helps maintain a clean codebase and simplifies deployment strategies.

Customizing API Responses

For a consistent API experience, a dedicated module (utils/api-response.ts) handles response formatting:

import { Response } from 'express';

/**
 * API Response Handler Module
 * Provides a standardized structure for all API responses.
 */
export default class ApiResponse {
  statusCode: number;
  data: object;
  message: string;
  success: boolean;

  constructor(statusCode: number, data: object, message = 'Success') {
    this.statusCode = statusCode;
    this.data = data;
    this.message = message;
    this.success = statusCode < 400;
  }
}

/**
 * Sends a standardized API response.
 */
export const sendResponse = (res: Response, statusCode: number, data: any, message: string): void => {
  res.status(statusCode).json(new ApiResponse(statusCode, data, message));
};

/**
 * Handles errors with a consistent format.
 */
export const handleError = (res: Response, error: any, defaultMessage: string = 'Something went wrong'): void => {
  console.error('Error:', error);
  const statusCode = error.statusCode || 500;
  const message = error.message || defaultMessage;
  sendResponse(res, statusCode, {}, message);
};

// Usage examples:
// sendResponse(res, 201, newUser.rows[0], 'User signed up successfully');
// handleError(res, error, 'Something went wrong while signing up');
Enter fullscreen mode Exit fullscreen mode

By encapsulating response logic, you enforce a uniform API contract, reducing the risk of inconsistent client-side behaviors and facilitating easier debugging and monitoring.


Final Thoughts

Reordering functions, mutations, queries, and use cases according to production needs ensures that each module serves a clear purpose. This advanced configuration supports robust scaling, fault tolerance, and continuous integration/deployment practices.

Key Takeaways:

  • Standardization: Implementing TypeScript, ESLint, and Prettier lays a solid foundation for consistency across a growing codebase.
  • Maintainability: A modular project structure coupled with automated tooling reduces overhead and technical debt.
  • Production Readiness: Adopting these best practices is not just about writing cleaner code it's about creating a sustainable environment that supports rapid scaling and innovation.

By leveraging this production-level setup, organizations can confidently drive technological initiatives while ensuring that engineering teams maintain peak efficiency and code quality.

Top comments (0)