Matter AI | Code Reviewer Documentation home pagelight logodark logo
  • Contact
  • Github
  • Sign in
  • Sign in
  • Documentation
  • Blog
  • Discord
  • Github
  • Introduction
    • What is Matter AI?
    Getting Started
    • QuickStart
    Product
    • Security Analysis
    • Code Quality
    • Agentic Chat
    • RuleSets
    • Memories
    • Analytics
    • Command List
    • Configurations
    Patterns
    • Languages
      • Supported Languages
      • Python
      • Java
      • JavaScript
      • TypeScript
      • Node.js
      • React
      • Fastify
      • Next.js
      • Terraform
      • C#
      • C++
      • C
      • Go
      • Rust
      • Swift
      • React Native
      • Spring Boot
      • Kotlin
      • Flutter
      • Ruby
      • PHP
      • Scala
      • Perl
      • R
      • Dart
      • Elixir
      • Erlang
      • Haskell
      • Lua
      • Julia
      • Clojure
      • Groovy
      • Fortran
      • COBOL
      • Pascal
      • Assembly
      • Bash
      • PowerShell
      • SQL
      • PL/SQL
      • T-SQL
      • MATLAB
      • Objective-C
      • VBA
      • ABAP
      • Apex
      • Apache Camel
      • Crystal
      • D
      • Delphi
      • Elm
      • F#
      • Hack
      • Lisp
      • OCaml
      • Prolog
      • Racket
      • Scheme
      • Solidity
      • Verilog
      • VHDL
      • Zig
      • MongoDB
      • ClickHouse
      • MySQL
      • GraphQL
      • Redis
      • Cassandra
      • Elasticsearch
    • Security
    • Performance
    Integrations
    • Code Repositories
    • Team Messengers
    • Ticketing
    Enterprise
    • Enterprise Deployment Overview
    • Enterprise Configurations
    • Observability and Fallbacks
    • Create Your Own GitHub App
    • Self-Hosting Options
    • RBAC
    Patterns
    Languages

    Node.js

    Node.js is a JavaScript runtime built on Chromes V8 JavaScript engine, enabling server-side JavaScript execution. It uses an event-driven, non-blocking I/O model that makes it lightweight and efficient for building scalable network applications.

    Node.js, despite its popularity and efficiency, has several common anti-patterns that can lead to performance issues, memory leaks, and maintenance problems. Here are the most important anti-patterns to avoid when writing Node.js code.

    // Anti-pattern: Not handling errors
    fs.readFile('/file-path.txt', (err, data) => {
      // No error handling
      const content = data.toString();
      console.log(content);
    });
    
    // Better approach: Handle errors properly
    fs.readFile('/file-path.txt', (err, data) => {
      if (err) {
        console.error('Error reading file:', err);
        return;
      }
      
      const content = data.toString();
      console.log(content);
    });
    
    // Even better: Use promises with async/await and try/catch
    async function readFileContent() {
      try {
        const data = await fs.promises.readFile('/file-path.txt');
        const content = data.toString();
        console.log(content);
      } catch (err) {
        console.error('Error reading file:', err);
      }
    }

    Not handling errors can lead to application crashes and unpredictable behavior. Always check for errors in callbacks or use try/catch with async/await.

    // Anti-pattern: Callback hell (Pyramid of Doom)
    function processData() {
      fetchData(function(err, data) {
        if (err) {
          console.error(err);
          return;
        }
        
        processFirstStep(data, function(err, firstResult) {
          if (err) {
            console.error(err);
            return;
          }
          
          processSecondStep(firstResult, function(err, secondResult) {
            if (err) {
              console.error(err);
              return;
            }
            
            processFinalStep(secondResult, function(err, finalResult) {
              if (err) {
                console.error(err);
                return;
              }
              
              console.log('Final result:', finalResult);
            });
          });
        });
      });
    }
    
    // Better approach: Use Promises
    function processData() {
      return fetchData()
        .then(data => processFirstStep(data))
        .then(firstResult => processSecondStep(firstResult))
        .then(secondResult => processFinalStep(secondResult))
        .then(finalResult => {
          console.log('Final result:', finalResult);
          return finalResult;
        })
        .catch(err => {
          console.error('Error in processing:', err);
          throw err;
        });
    }
    
    // Even better: Use async/await
    async function processData() {
      try {
        const data = await fetchData();
        const firstResult = await processFirstStep(data);
        const secondResult = await processSecondStep(firstResult);
        const finalResult = await processFinalStep(secondResult);
        
        console.log('Final result:', finalResult);
        return finalResult;
      } catch (err) {
        console.error('Error in processing:', err);
        throw err;
      }
    }

    Deeply nested callbacks make code hard to read and maintain. Use Promises or async/await to write more linear, readable code.

    // Anti-pattern: Loading entire file into memory
    function processLargeFile(filePath) {
      fs.readFile(filePath, (err, data) => {
        if (err) {
          console.error(err);
          return;
        }
        
        // Process entire file in memory
        const lines = data.toString().split('\n');
        lines.forEach(line => {
          // Process each line
        });
      });
    }
    
    // Better approach: Use streams
    function processLargeFile(filePath) {
      const readStream = fs.createReadStream(filePath, { encoding: 'utf8' });
      const lineReader = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity
      });
      
      lineReader.on('line', (line) => {
        // Process each line without loading entire file
      });
      
      lineReader.on('close', () => {
        console.log('File processing completed');
      });
      
      readStream.on('error', (err) => {
        console.error('Error reading file:', err);
      });
    }

    Loading large files entirely into memory can cause out-of-memory errors. Use streams to process data in chunks.

    // Anti-pattern: Blocking the event loop
    function calculateFibonacci(n) {
      if (n <= 1) return n;
      return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
    }
    
    app.get('/fibonacci/:n', (req, res) => {
      const n = parseInt(req.params.n);
      // This blocks the event loop for large values of n
      const result = calculateFibonacci(n);
      res.send({ result });
    });
    
    // Better approach: Use worker threads for CPU-intensive tasks
    const { Worker } = require('worker_threads');
    
    app.get('/fibonacci/:n', (req, res) => {
      const n = parseInt(req.params.n);
      
      const worker = new Worker(`
        const { parentPort, workerData } = require('worker_threads');
        
        function calculateFibonacci(n) {
          if (n <= 1) return n;
          return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
        }
        
        parentPort.postMessage(calculateFibonacci(workerData));
      `, { eval: true, workerData: n });
      
      worker.on('message', (result) => {
        res.send({ result });
      });
      
      worker.on('error', (err) => {
        res.status(500).send({ error: err.message });
      });
    });

    CPU-intensive operations block the event loop, affecting all requests. Use worker threads for CPU-bound tasks.

    // Anti-pattern: Creating memory leaks
    const cache = {};
    
    app.get('/data/:id', (req, res) => {
      const id = req.params.id;
      
      // This cache grows unbounded
      if (!cache[id]) {
        cache[id] = fetchData(id);
      }
      
      res.send(cache[id]);
    });
    
    // Better approach: Use a proper caching mechanism with limits
    const LRU = require('lru-cache');
    const cache = new LRU({
      max: 500, // Maximum items in cache
      maxAge: 1000 * 60 * 60 // Items expire after 1 hour
    });
    
    app.get('/data/:id', (req, res) => {
      const id = req.params.id;
      
      if (!cache.has(id)) {
        const data = fetchData(id);
        cache.set(id, data);
      }
      
      res.send(cache.get(id));
    });

    Unbounded caches and event listeners are common sources of memory leaks. Use proper caching libraries and always remove event listeners when they’re no longer needed.

    // Anti-pattern: Hardcoded configuration
    const dbConfig = {
      host: 'production-db.example.com',
      user: 'admin',
      password: 'supersecretpassword',
      database: 'myapp'
    };
    
    const apiKey = 'abcd1234efgh5678';
    
    // Better approach: Use environment variables
    require('dotenv').config(); // Load .env file if using dotenv
    
    const dbConfig = {
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME
    };
    
    const apiKey = process.env.API_KEY;

    Hardcoded configuration values make deployment difficult and pose security risks. Use environment variables for configuration.

    // Anti-pattern: Not handling promise rejections
    function fetchData() {
      return fetch('https://api.example.com/data')
        .then(response => response.json())
        .then(data => processData(data));
      // No .catch() to handle errors
    }
    
    // Better approach: Always catch promise errors
    function fetchData() {
      return fetch('https://api.example.com/data')
        .then(response => {
          if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
          }
          return response.json();
        })
        .then(data => processData(data))
        .catch(error => {
          console.error('Error fetching data:', error);
          // Handle error appropriately
          throw error; // Re-throw or return a default value
        });
    }

    Unhandled promise rejections can cause application crashes. Always add .catch() to promise chains or use try/catch with async/await.

    // Anti-pattern: Using synchronous operations in async code
    app.get('/data', (req, res) => {
      // This blocks the event loop
      const data = fs.readFileSync('/path/to/data.json');
      const parsedData = JSON.parse(data);
      res.json(parsedData);
    });
    
    // Better approach: Use asynchronous operations
    app.get('/data', async (req, res) => {
      try {
        const data = await fs.promises.readFile('/path/to/data.json');
        const parsedData = JSON.parse(data);
        res.json(parsedData);
      } catch (err) {
        console.error('Error reading file:', err);
        res.status(500).send('Server error');
      }
    });

    Synchronous operations block the event loop and reduce throughput. Always use asynchronous versions of I/O operations.

    // Anti-pattern: Running Node directly
    // $ node server.js
    
    // Better approach: Use a process manager
    // $ pm2 start server.js --name "my-app" -i max
    
    // In package.json
    {
      "scripts": {
        "start": "node server.js",
        "start:prod": "pm2 start server.js --name 'my-app' -i max",
        "stop": "pm2 stop my-app",
        "restart": "pm2 restart my-app"
      }
    }

    Running Node.js applications directly in production doesn’t handle crashes or utilize multiple cores. Use process managers like PM2 or Docker containers.

    // Anti-pattern: Not validating user input
    app.post('/users', (req, res) => {
      const { name, email, age } = req.body;
      
      // Directly using unvalidated input
      db.createUser({ name, email, age })
        .then(user => res.status(201).json(user))
        .catch(err => res.status(500).json({ error: err.message }));
    });
    
    // Better approach: Validate input
    const { body, validationResult } = require('express-validator');
    
    app.post('/users', [
      body('name').isString().trim().isLength({ min: 2, max: 50 }),
      body('email').isEmail().normalizeEmail(),
      body('age').isInt({ min: 0, max: 120 })
    ], (req, res) => {
      const errors = validationResult(req);
      
      if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
      }
      
      const { name, email, age } = req.body;
      
      db.createUser({ name, email, age })
        .then(user => res.status(201).json(user))
        .catch(err => res.status(500).json({ error: err.message }));
    });

    Not validating user input can lead to security vulnerabilities and data corruption. Always validate and sanitize user input.

    // Anti-pattern: No security headers
    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      res.send('Hello World!');
    });
    
    // Better approach: Use security middleware
    const express = require('express');
    const helmet = require('helmet');
    const app = express();
    
    // Add security headers
    app.use(helmet());
    
    app.get('/', (req, res) => {
      res.send('Hello World!');
    });

    Not using security headers makes your application vulnerable to various attacks. Use middleware like Helmet to add appropriate security headers.

    // Anti-pattern: Not handling uncaught exceptions
    // No global error handlers
    
    // Better approach: Handle uncaught exceptions and unhandled rejections
    process.on('uncaughtException', (err) => {
      console.error('Uncaught Exception:', err);
      // Log to error monitoring service
      // Gracefully shutdown or restart
      process.exit(1);
    });
    
    process.on('unhandledRejection', (reason, promise) => {
      console.error('Unhandled Rejection at:', promise, 'reason:', reason);
      // Log to error monitoring service
    });

    Unhandled exceptions can crash your application. Set up global error handlers to log errors and gracefully shut down.

    // Anti-pattern: Using console.log for everything
    function processOrder(order) {
      console.log('Processing order:', order);
      // Process order
      console.log('Order processed successfully');
    }
    
    // Better approach: Use a proper logging library
    const winston = require('winston');
    
    const logger = winston.createLogger({
      level: process.env.LOG_LEVEL || 'info',
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
      ),
      transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
      ]
    });
    
    function processOrder(order) {
      logger.info('Processing order', { orderId: order.id });
      // Process order
      logger.info('Order processed successfully', { orderId: order.id });
    }

    Using console.log for logging doesn’t provide features like log levels, formatting, and output configuration. Use a proper logging library like Winston or Pino.

    TypeScriptReact
    websitexgithublinkedin
    Powered by Mintlify
    Assistant
    Responses are generated using AI and may contain mistakes.