Exemples Fastify

Exemples du framework web Node.js haute performance Fastify incluant les plugins, hooks, validation et techniques d'optimisation

Key Facts

Category
Web Frameworks
Items
1
Format Families
sample

Sample Overview

Exemples du framework web Node.js haute performance Fastify incluant les plugins, hooks, validation et techniques d'optimisation This sample set belongs to Web Frameworks and can be used to test related workflows inside Elysia Tools.

💻 Bonjour Monde Fastify javascript

🟢 simple

Configuration de base du serveur Fastify avec routage, middleware et fonctionnalités essentielles pour les applications web haute performance

// Fastify Hello World Examples

// 1. Basic Fastify Server
var fastify = require('fastify')({ logger: true });

// Declare a route
fastify.get('/', async (request, reply) => {
  return { hello: 'world' };
});

// Run the server
var start = async () => {
  try {
    await fastify.listen(3000);
    console.log('Server listening on http://localhost:3000');
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

// 2. Fastify with TypeScript
// src/server.ts
import fastifyTs from 'fastify';
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';

const server: FastifyInstance = fastifyTs({
  logger: {
    level: 'info',
    prettyPrint: true,
  },
});

// Define routes
server.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
  return {
    message: 'Hello Fastify with TypeScript!',
    timestamp: new Date().toISOString(),
    version: '1.0.0'
  };
});

server.get('/health', async (request: FastifyRequest, reply: FastifyReply) => {
  return {
    status: 'ok',
    uptime: process.uptime(),
    memory: process.memoryUsage()
  };
});

// Start server
var start = async () => {
  try {
    const port = parseInt(process.env.PORT || '3000');
    const host = process.env.HOST || '0.0.0.0';

    await server.listen({ port, host });
    console.log(`🚀 Server ready at http://${host}:${port}`);
  } catch (err) {
    server.log.error(err);
    process.exit(1);
  }
};

start();

// 3. Fastify with Route Parameters and Validation
var fastify = require('fastify')({ logger: true });

// User data store (in production, use a database)
const users = [
  { id: 1, name: 'John Doe', email: '[email protected]', age: 30 },
  { id: 2, name: 'Jane Smith', email: '[email protected]', age: 25 },
  { id: 3, name: 'Bob Johnson', email: '[email protected]', age: 35 }
];

// Define schemas for validation
const userSchema = {
  type: 'object',
  properties: {
    id: { type: 'number' },
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'number', minimum: 0, maximum: 150 }
  },
  required: ['name', 'email']
};

const createUserSchema = {
  type: 'object',
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'number', minimum: 0, maximum: 150 }
  },
  required: ['name', 'email']
};

// Routes with validation
fastify.get('/api/users', {
  schema: {
    description: 'Get all users',
    tags: ['users'],
    response: {
      200: {
        type: 'object',
        properties: {
          users: {
            type: 'array',
            items: userSchema
          },
          total: { type: 'number' }
        },
        required: ['users', 'total']
      }
    }
  }
}, async (request, reply) => {
  return { users, total: users.length };
});

fastify.get('/api/users/:id', {
  schema: {
    description: 'Get user by ID',
    tags: ['users'],
    params: {
      type: 'object',
      properties: {
        id: { type: 'number' }
      }
    },
    response: {
      200: userSchema,
      404: {
        type: 'object',
        properties: {
          error: { type: 'string' }
        }
      }
    }
  }
}, async (request, reply) => {
  const { id } = request.params;
  const user = users.find(u => u.id === id);

  if (!user) {
    reply.code(404).send({ error: 'User not found' });
    return;
  }

  return user;
});

fastify.post('/api/users', {
  schema: {
    description: 'Create a new user',
    tags: ['users'],
    body: createUserSchema,
    response: {
      201: userSchema,
      400: {
        type: 'object',
        properties: {
          error: { type: 'string' }
        }
      }
    }
  }
}, async (request, reply) => {
  const newUser = {
    id: users.length + 1,
    ...request.body
  };

  users.push(newUser);
  reply.code(201).send(newUser);
});

// 4. Fastify with Hooks and Middleware
var fastify = require('fastify')({ logger: true });

// Global hooks
fastify.addHook('onRequest', async (request, reply) => {
  const start = Date.now();
  request.startTime = start;
});

fastify.addHook('onResponse', async (request, reply) => {
  const duration = Date.now() - request.startTime;
  fastify.log.info(`Request to ${request.url} took ${duration}ms`);
});

// Authentication hook
fastify.addHook('preHandler', async (request, reply) => {
  // Skip auth for certain routes
  if (request.url.startsWith('/public') || request.url === '/') {
    return;
  }

  const authHeader = request.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    reply.code(401).send({ error: 'Unauthorized' });
    return;
  }

  // In production, verify JWT token here
  const token = authHeader.substring(7);
  if (token !== 'valid-token') {
    reply.code(401).send({ error: 'Invalid token' });
    return;
  }

  // Add user info to request
  request.user = { id: 1, name: 'Authenticated User' };
});

// Public routes
fastify.get('/public/info', async (request, reply) => {
  return { message: 'This is public information', timestamp: new Date().toISOString() };
});

// Protected routes
fastify.get('/protected/data', async (request, reply) => {
  return {
    message: 'This is protected data',
    user: request.user,
    timestamp: new Date().toISOString()
  };
});

// 5. Fastify with Plugins
// plugins/auth.js
const fp = require('fastify-plugin');

async function authPlugin(fastify, options) {
  fastify.decorate('authenticate', async (request, reply) => {
    try {
      await request.jwtVerify();
    } catch (err) {
      reply.send(err);
    }
  });
}

module.exports = fp(authPlugin);

// plugins/database.js
const fp = require('fastify-plugin');

async function databasePlugin(fastify, options) {
  // Mock database connection
  const db = {
    users: [],
    posts: [],

    async getUser(id) {
      return this.users.find(user => user.id === id);
    },

    async createUser(userData) {
      const user = { id: this.users.length + 1, ...userData };
      this.users.push(user);
      return user;
    }
  };

  fastify.decorate('db', db);
}

module.exports = fp(databasePlugin);

// Main server with plugins
var fastify = require('fastify')({ logger: true });

// Register plugins
fastify.register(require('fastify-jwt'), { secret: 'supersecret' });
fastify.register(require('./plugins/auth'));
fastify.register(require('./plugins/database'));

// Routes using plugins
fastify.get('/api/profile', {
  preHandler: [fastify.authenticate]
}, async (request, reply) => {
  const user = await fastify.db.getUser(request.user.id);
  return { user };
});

fastify.post('/api/users', async (request, reply) => {
  const user = await fastify.db.createUser(request.body);
  return user;
});

// 6. Fastify with File Uploads
var fastify = require('fastify')({ logger: true });
const path = require('path');
const fs = require('fs').promises;

// Register multipart plugin for file uploads
fastify.register(require('fastify-multipart'));

// Ensure upload directory exists
const uploadDir = path.join(__dirname, 'uploads');
fs.mkdir(uploadDir, { recursive: true }).catch(console.error);

// File upload route
fastify.post('/upload', async (request, reply) => {
  try {
    const data = await request.file();

    if (!data) {
      reply.code(400).send({ error: 'No file uploaded' });
      return;
    }

    // Generate unique filename
    const filename = `${Date.now()}-${data.filename}`;
    const filepath = path.join(uploadDir, filename);

    // Save file
    await pump(data.file, fs.createWriteStream(filepath));

    reply.send({
      message: 'File uploaded successfully',
      filename,
      size: data.file.bytesRead
    });
  } catch (error) {
    fastify.log.error(error);
    reply.code(500).send({ error: 'File upload failed' });
  }
});

// Multiple files upload
fastify.post('/upload/multiple', async (request, reply) => {
  try {
    const files = [];
    const parts = request.files();

    for await (const part of parts) {
      const filename = `${Date.now()}-${part.filename}`;
      const filepath = path.join(uploadDir, filename);

      await pump(part.file, fs.createWriteStream(filepath));

      files.push({
        filename,
        originalName: part.filename,
        size: part.file.bytesRead
      });
    }

    reply.send({
      message: 'Files uploaded successfully',
      files,
      count: files.length
    });
  } catch (error) {
    fastify.log.error(error);
    reply.code(500).send({ error: 'Multiple file upload failed' });
  }
});

// 7. Fastify with Error Handling
var fastify = require('fastify')({ logger: true });

// Custom error class
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
  }
}

// Set error handler
fastify.setErrorHandler(function (error, request, reply) {
  // Log error
  this.log.error(error);

  // Send error response
  if (error.validation) {
    // Validation error
    reply.code(400).send({
      error: 'Validation Error',
      details: error.validation,
      statusCode: 400
    });
    return;
  }

  if (error.isOperational) {
    // Operational error (expected)
    reply.code(error.statusCode || 500).send({
      error: error.message,
      statusCode: error.statusCode || 500
    });
  } else {
    // Programming error
    reply.code(500).send({
      error: 'Internal Server Error',
      statusCode: 500
    });
  }
});

// Route that throws different errors
fastify.get('/error/validation', {
  schema: {
    querystring: {
      type: 'object',
      properties: {
        name: { type: 'string', minLength: 3 }
      },
      required: ['name']
    }
  }
}, async (request, reply) => {
  // This will cause validation error if name is too short
  return { message: `Hello ${request.query.name}` };
});

fastify.get('/error/operational', async (request, reply) => {
  throw new AppError('This is an operational error', 400);
});

fastify.get('/error/programming', async (request, reply) => {
  // This will be caught as a programming error
  throw new Error('Unexpected error occurred');
});

// 8. Fastify with Environment Configuration
var fastify = require('fastify')({
  logger: {
    level: process.env.LOG_LEVEL || 'info'
  }
});

// Configuration
const config = {
  port: parseInt(process.env.PORT || 3000),
  host: process.env.HOST || '0.0.0.0',
  database: {
    url: process.env.DATABASE_URL || 'mongodb://localhost:27017/fastify',
    name: process.env.DATABASE_NAME || 'fastify_db'
  },
  jwt: {
    secret: process.env.JWT_SECRET || 'fallback-secret-key'
  },
  cors: {
    origin: process.env.CORS_ORIGIN || '*',
    credentials: true
  }
};

// Register environment-specific plugins
if (process.env.NODE_ENV === 'development') {
  fastify.register(require('fastify-cors'), config.cors);
}

// Health check endpoint
fastify.get('/health', async (request, reply) => {
  return {
    status: 'ok',
    environment: process.env.NODE_ENV || 'development',
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    config: {
      port: config.port,
      databaseName: config.database.name
    }
  };
});

// 9. Fastify with Testing Setup
// test/server.test.js
const { test } = require('tap');
const build = require('../server');

test('GET / route', async t => {
  const app = build();

  const response = await app.inject({
    method: 'GET',
    url: '/'
  });

  t.equal(response.statusCode, 200);
  t.same(response.json(), { hello: 'world' });
});

test('POST /api/users with valid data', async t => {
  const app = build();

  const userData = {
    name: 'Test User',
    email: '[email protected]',
    age: 25
  };

  const response = await app.inject({
    method: 'POST',
    url: '/api/users',
    payload: userData
  });

  t.equal(response.statusCode, 201);
  t.match(response.json(), userData);
});

test('POST /api/users with invalid data', async t => {
  const app = build();

  const invalidData = {
    name: '',  // Invalid: empty string
    email: 'invalid-email'  // Invalid: not a valid email
  };

  const response = await app.inject({
    method: 'POST',
    url: '/api/users',
    payload: invalidData
  });

  t.equal(response.statusCode, 400);
  t.ok(response.json().validation);
});

// 10. Fastify Production Best Practices
var fastify = require('fastify')({
  logger: {
    level: process.env.LOG_LEVEL || 'info',
    stream: process.stdout
  },
  // Enable trust proxy for proper IP logging behind reverse proxy
  trustProxy: true
});

// Graceful shutdown
process.on('SIGINT', async () => {
  fastify.log.info('Received SIGINT, shutting down gracefully...');
  await fastify.close();
  process.exit(0);
});

process.on('SIGTERM', async () => {
  fastify.log.info('Received SIGTERM, shutting down gracefully...');
  await fastify.close();
  process.exit(0);
});

// Health check for load balancers
fastify.get('/health', {
  schema: {
    hide: true  // Hide from documentation
  }
}, async (request, reply) => {
  return { status: 'ok' };
});

// Production-ready startup
var start = async () => {
  try {
    const port = parseInt(process.env.PORT || 3000);

    await fastify.listen({
      port,
      host: '0.0.0.0'  // Listen on all interfaces
    });

    fastify.log.info(`🚀 Server ready on port ${port}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();