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

    Fastify

    Fastify is a web framework for Node.js focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is one of the fastest web frameworks available for Node.js.

    Fastify, despite its performance and plugin-based architecture, has several common anti-patterns that can lead to performance issues, maintenance problems, and security vulnerabilities. Here are the most important anti-patterns to avoid when developing with Fastify.

    // Anti-pattern: Not using schema validation
    fastify.post('/users', async (request, reply) => {
      const { name, email, age } = request.body;
      
      // Manual validation
      if (!name || typeof name !== 'string') {
        reply.code(400).send({ error: 'Invalid name' });
        return;
      }
      
      if (!email || !email.includes('@')) {
        reply.code(400).send({ error: 'Invalid email' });
        return;
      }
      
      if (!age || typeof age !== 'number' || age < 0) {
        reply.code(400).send({ error: 'Invalid age' });
        return;
      }
      
      // Process the request
      // ...
    });
    
    // Better approach: Use schema validation
    const userSchema = {
      body: {
        type: 'object',
        required: ['name', 'email', 'age'],
        properties: {
          name: { type: 'string', minLength: 2 },
          email: { type: 'string', format: 'email' },
          age: { type: 'integer', minimum: 0 }
        }
      }
    };
    
    fastify.post('/users', { schema: userSchema }, async (request, reply) => {
      // Body is already validated, no need for manual checks
      const { name, email, age } = request.body;
      
      // Process the request
      // ...
    });

    Manual validation is error-prone and verbose. Use Fastify’s built-in schema validation based on JSON Schema to automatically validate requests and generate documentation.

    // Anti-pattern: Poor error handling
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      if (!user) {
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      return user;
    });
    
    // Better approach: Use custom error classes and error handler
    class NotFoundError extends Error {
      constructor(message) {
        super(message);
        this.statusCode = 404;
      }
    }
    
    fastify.setErrorHandler((error, request, reply) => {
      if (error instanceof NotFoundError) {
        reply.code(error.statusCode).send({ error: error.message });
        return;
      }
      
      // Log the error
      request.log.error(error);
      
      // Send a generic error response in production
      if (process.env.NODE_ENV === 'production') {
        reply.code(500).send({ error: 'Internal Server Error' });
        return;
      }
      
      // Send detailed error in development
      reply.code(500).send({ error: error.message, stack: error.stack });
    });
    
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      if (!user) {
        throw new NotFoundError('User not found');
      }
      return user;
    });

    Inconsistent error handling leads to poor user experience and security issues. Use custom error classes and Fastify’s error handler to centralize error handling logic.

    // Anti-pattern: Mixing callbacks and async/await
    fastify.get('/users/:id', async (request, reply) => {
      db.users.findById(request.params.id, (err, user) => {
        if (err) {
          reply.code(500).send({ error: err.message });
          return;
        }
        
        if (!user) {
          reply.code(404).send({ error: 'User not found' });
          return;
        }
        
        reply.send(user);
      });
    });
    
    // Better approach: Consistent async/await
    fastify.get('/users/:id', async (request, reply) => {
      try {
        const user = await db.users.findById(request.params.id);
        if (!user) {
          return reply.code(404).send({ error: 'User not found' });
        }
        return user; // Fastify automatically handles the response
      } catch (err) {
        request.log.error(err);
        throw err; // Let the error handler deal with it
      }
    });

    Mixing callbacks with async/await makes code harder to read and reason about. Be consistent with async/await throughout your route handlers.

    // Anti-pattern: Global plugin registration without scoping
    // server.js
    const fastify = require('fastify')();
    
    // Register plugins globally
    fastify.register(require('fastify-cors'));
    fastify.register(require('fastify-jwt'), { secret: 'supersecret' });
    
    // Routes
    fastify.register(require('./routes/public'));
    fastify.register(require('./routes/admin'));
    fastify.register(require('./routes/user'));
    
    // Better approach: Scope plugins to where they're needed
    // server.js
    const fastify = require('fastify')();
    
    // Register common plugins
    fastify.register(require('./plugins/common'));
    
    // Register route groups with their specific plugins
    fastify.register(function publicRoutes(instance, opts, done) {
      // Public routes don't need authentication
      instance.register(require('./routes/public'));
      done();
    }, { prefix: '/api/public' });
    
    fastify.register(function protectedRoutes(instance, opts, done) {
      // Protected routes need JWT authentication
      instance.register(require('fastify-jwt'), { secret: 'supersecret' });
      
      // Add authentication hook
      instance.addHook('onRequest', async (request, reply) => {
        try {
          await request.jwtVerify();
        } catch (err) {
          reply.code(401).send({ error: 'Unauthorized' });
        }
      });
      
      instance.register(require('./routes/admin'), { prefix: '/admin' });
      instance.register(require('./routes/user'), { prefix: '/user' });
      
      done();
    }, { prefix: '/api' });

    Registering all plugins globally can lead to unnecessary overhead and potential security issues. Use Fastify’s encapsulation to scope plugins only to the routes that need them.

    // Anti-pattern: Duplicating logic in route handlers
    fastify.get('/users/:id', async (request, reply) => {
      // Authentication check
      try {
        await request.jwtVerify();
      } catch (err) {
        reply.code(401).send({ error: 'Unauthorized' });
        return;
      }
      
      // Log the request
      request.log.info(`Fetching user ${request.params.id}`);
      
      // Actual handler logic
      const user = await db.users.findById(request.params.id);
      if (!user) {
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      return user;
    });
    
    // Better approach: Use hooks for cross-cutting concerns
    // Authentication hook
    fastify.register(async (instance) => {
      instance.addHook('onRequest', async (request, reply) => {
        try {
          await request.jwtVerify();
        } catch (err) {
          reply.code(401).send({ error: 'Unauthorized' });
        }
      });
      
      // Logging hook
      instance.addHook('preHandler', async (request, reply) => {
        request.log.info(`Processing ${request.method} ${request.url}`);
      });
      
      // Register routes that need these hooks
      instance.get('/users/:id', async (request, reply) => {
        // Focus only on the business logic
        const user = await db.users.findById(request.params.id);
        if (!user) {
          reply.code(404).send({ error: 'User not found' });
          return;
        }
        return user;
      });
      
      // More routes...
    });

    Duplicating cross-cutting concerns in each route handler leads to code duplication and maintenance issues. Use Fastify’s hooks to centralize common logic like authentication, logging, and error handling.

    // Anti-pattern: Manual response transformation
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      if (!user) {
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      
      // Manual transformation of the response
      return {
        id: user.id,
        name: user.name,
        email: user.email,
        // Omit sensitive fields like password, internal notes, etc.
      };
    });
    
    // Better approach: Use response schema with serialization
    const userSchema = {
      response: {
        200: {
          type: 'object',
          properties: {
            id: { type: 'string' },
            name: { type: 'string' },
            email: { type: 'string' }
            // No sensitive fields defined here
          }
        }
      }
    };
    
    fastify.get('/users/:id', { schema: userSchema }, async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      if (!user) {
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      
      // Return the full user object - serialization will filter fields
      return user;
    });

    Manual response transformation is error-prone and can lead to inconsistent APIs. Use Fastify’s response schemas to automatically serialize responses and filter out sensitive fields.

    // Anti-pattern: Inconsistent response patterns
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      
      if (!user) {
        // Using reply.send()
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      
      // Directly returning the object
      return user;
    });
    
    // Better approach: Consistent response pattern
    // Option 1: Always use return
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      
      if (!user) {
        return reply.code(404).send({ error: 'User not found' });
      }
      
      return user;
    });
    
    // Option 2: Always use reply.send()
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      
      if (!user) {
        reply.code(404).send({ error: 'User not found' });
        return;
      }
      
      reply.send(user);
    });

    Mixing response patterns makes code harder to read and maintain. Choose one consistent pattern for handling responses throughout your application.

    // Anti-pattern: Not configuring content type parsers
    const fastify = require('fastify')();
    
    fastify.post('/upload', async (request, reply) => {
      // This will fail for multipart/form-data
      const { file } = request.body;
      // ...
    });
    
    // Better approach: Configure appropriate parsers
    const fastify = require('fastify')();
    const multipart = require('@fastify/multipart');
    
    // Register multipart support
    fastify.register(multipart, {
      limits: {
        fileSize: 1024 * 1024 * 10 // 10MB
      }
    });
    
    // Handle file uploads
    fastify.post('/upload', async (request, reply) => {
      const data = await request.file();
      
      // Process the file
      const buffer = await data.toBuffer();
      // ...
      
      return { uploaded: true, filename: data.filename };
    });
    
    // Handle multiple files
    fastify.post('/upload-many', async (request, reply) => {
      const files = await request.files();
      
      for (const part of files) {
        // Process each file
        // ...
      }
      
      return { uploaded: files.length };
    });

    Not configuring appropriate content type parsers leads to errors when handling different types of requests. Register the appropriate parsers for your API’s needs.

    // Anti-pattern: Using global variables or passing data through function parameters
    const db = require('./db');
    
    fastify.get('/users', async (request, reply) => {
      return db.users.findAll();
    });
    
    fastify.get('/users/:id', async (request, reply) => {
      return db.users.findById(request.params.id);
    });
    
    // Better approach: Use decorators to attach shared resources
    const fastify = require('fastify')();
    
    // Database plugin
    fastify.register(async (instance, opts) => {
      const db = require('./db');
      
      // Make db available to all routes in this context
      instance.decorate('db', db);
      
      // Close database connection when the server closes
      instance.addHook('onClose', async (instance) => {
        await db.close();
      });
    });
    
    // Use the decorator in routes
    fastify.register(async (instance, opts) => {
      instance.get('/users', async (request, reply) => {
        return request.server.db.users.findAll();
      });
      
      instance.get('/users/:id', async (request, reply) => {
        return request.server.db.users.findById(request.params.id);
      });
    });

    Using global variables makes testing difficult and can lead to unexpected behavior. Use Fastify’s decorators to attach shared resources to the server, request, or reply objects.

    // Anti-pattern: Using console.log
    fastify.get('/users/:id', async (request, reply) => {
      console.log(`Getting user ${request.params.id}`);
      
      try {
        const user = await db.users.findById(request.params.id);
        if (!user) {
          console.log(`User ${request.params.id} not found`);
          reply.code(404).send({ error: 'User not found' });
          return;
        }
        console.log(`Found user ${user.id}`);
        return user;
      } catch (err) {
        console.error('Error fetching user:', err);
        reply.code(500).send({ error: 'Internal Server Error' });
      }
    });
    
    // Better approach: Use Fastify's logger
    // Configure Fastify with appropriate logger
    const fastify = require('fastify')({
      logger: {
        level: process.env.LOG_LEVEL || 'info',
        serializers: {
          req(request) {
            return {
              method: request.method,
              url: request.url,
              headers: request.headers,
              hostname: request.hostname,
              remoteAddress: request.ip,
              remotePort: request.socket.remotePort
            };
          }
        }
      }
    });
    
    fastify.get('/users/:id', async (request, reply) => {
      request.log.info({ params: request.params }, 'Getting user');
      
      try {
        const user = await db.users.findById(request.params.id);
        if (!user) {
          request.log.info({ userId: request.params.id }, 'User not found');
          reply.code(404).send({ error: 'User not found' });
          return;
        }
        request.log.info({ userId: user.id }, 'User found');
        return user;
      } catch (err) {
        request.log.error(err, 'Error fetching user');
        reply.code(500).send({ error: 'Internal Server Error' });
      }
    });

    Using console.log doesn’t provide structured logging or log levels. Use Fastify’s built-in logger for structured, configurable logging.

    // Anti-pattern: Hardcoded configuration
    const fastify = require('fastify')();
    
    fastify.register(require('fastify-jwt'), {
      secret: 'supersecretkey'
    });
    
    fastify.listen(3000, '0.0.0.0', (err) => {
      if (err) throw err;
      console.log('Server listening on port 3000');
    });
    
    // Better approach: Use environment variables
    const fastify = require('fastify')({
      logger: process.env.NODE_ENV === 'development'
    });
    
    // Load environment variables
    require('dotenv').config();
    
    fastify.register(require('fastify-jwt'), {
      secret: process.env.JWT_SECRET
    });
    
    const start = async () => {
      try {
        await fastify.listen({
          port: process.env.PORT || 3000,
          host: process.env.HOST || '0.0.0.0'
        });
      } catch (err) {
        fastify.log.error(err);
        process.exit(1);
      }
    };
    
    start();

    Hardcoded configuration makes deployment difficult and poses security risks. Use environment variables for configuration and provide sensible defaults.

    // Anti-pattern: Using plain JavaScript without type checking
    const fastify = require('fastify')();
    
    fastify.get('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      return user;
    });
    
    // Better approach: Use TypeScript
    // user.ts
    interface User {
      id: string;
      name: string;
      email: string;
      age: number;
    }
    
    // server.ts
    import fastify, { FastifyRequest, FastifyReply } from 'fastify';
    import { User } from './types';
    
    const server = fastify();
    
    interface GetUserParams {
      id: string;
    }
    
    server.get<{
      Params: GetUserParams;
      Reply: User | { error: string };
    }>('/users/:id', async (request, reply) => {
      const user = await db.users.findById(request.params.id);
      if (!user) {
        return reply.code(404).send({ error: 'User not found' });
      }
      return user;
    });

    Not using TypeScript can lead to runtime errors and makes refactoring more difficult. Use TypeScript to catch errors at compile time and improve code maintainability.

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