npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fastynest

v0.0.8

Published

A Fastify framework project

Readme

fastynest Framework

⚠️ EXPERIMENTAL - ALPHA VERSION ⚠️

A powerful TypeScript decorator-based web framework built on top of Fastify. This framework provides a clean, modern approach to building REST APIs with built-in validation, authentication, file upload handling, and middleware management.

Features

  • 🚀 Decorator-based routing - Clean and intuitive route definitions
  • 🛡️ Built-in authentication & guards - Flexible security layer
  • 📋 Zod validation - Type-safe request/response validation
  • 📁 File upload handling - Single and multiple file uploads with validation
  • 🔧 Global middleware - Framework-wide request/response handling
  • 🏷️ Custom headers - Per-route and class-level header management
  • Fastify powered - High performance HTTP server

Quick Start

1. Basic Controller

import { Get, Post, Body, Query, Params } from './src/lib/framework';
import { z } from 'zod';

class UserController {
  @Get('/users')
  async getAllUsers() {
    return { users: ['John', 'Jane'] };
  }

  @Get('/users/:id')
  async getUser(@Params(z.object({ id: z.string() })) params: any) {
    const { id } = params;
    return { user: { id, name: 'John Doe' } };
  }

  @Post('/users')
  @Body(z.object({
    name: z.string().min(2),
    email: z.string().email()
  }))
  async createUser(req, reply) {
    const { name, email } = req.body;
    return { user: { id: '123', name, email } };
  }
}

2. Register Controller

import Fastify from 'fastify';
import { registerController } from './src/lib/framework';

const app = Fastify();

// Register single controller
registerController(app, UserController, '/api');

app.listen({ port: 3000 }, () => {
  console.log('Server running on port 3000');
});

API Reference

HTTP Method Decorators

@Get(path?: string)
@Post(path?: string)
@Put(path?: string)
@Delete(path?: string)
@Patch(path?: string)

Example:

class ProductController {
  @Get('/products')
  async list() { /* ... */ }

  @Post('/products')
  async create() { /* ... */ }

  @Put('/products/:id')
  async update() { /* ... */ }

  @Delete('/products/:id')
  async delete() { /* ... */ }
}

Validation Decorators

@Body(schema: ZodSchema)     // Validate request body
@Query(schema: ZodSchema)    // Validate query parameters
@Params(schema: ZodSchema)   // Validate route parameters

Example:

const CreateProductSchema = z.object({
  name: z.string().min(1),
  price: z.number().positive(),
  category: z.string()
});

const QuerySchema = z.object({
  page: z.string().transform(Number).optional(),
  limit: z.string().transform(Number).optional()
});

class ProductController {
  @Post('/products')
  async createProduct(
    @Body(CreateProductSchema) body: any,
    @Query(QuerySchema) query: any
  ) {
    // body is typed and validated
    // query is typed and validated
    const { name, price, category } = body;
    const { page = 1, limit = 10 } = query;

    return { product: { id: '123', name, price, category } };
  }
}

Authentication & Guards

@AuthGuard()           // Default Bearer token validation
@AuthGuard(customFn)   // Custom auth function
@UseGuard(guardFn)     // Custom guard function

Example:

// Custom auth guard
const apiKeyGuard = async (req, reply) => {
  const apiKey = req.headers['x-api-key'];
  return apiKey === 'valid-api-key';
};

// Admin role guard
const adminGuard = async (req, reply) => {
  const user = req.user; // Assuming user is set by auth middleware
  return user && user.role === 'admin';
};

@AuthGuard() // Apply to entire class
class UserController {
  @Get('/users')
  async getUsers() { /* Only authenticated users */ }

  @Delete('/users/:id')
  @UseGuard(adminGuard) // Additional guard for this route
  async deleteUser() { /* Only admin users */ }
}

File Upload

@FileUpload(config)                    // Custom file upload configuration
@SingleFile(fieldName, options)       // Single file upload
@MultipleFiles(options)                // Multiple files upload

Single File Upload:

class FileController {
  @Post('/upload')
  @SingleFile('document', {
    maxFileSize: 5 * 1024 * 1024, // 5MB
    allowedMimeTypes: ['image/jpeg', 'image/png', 'application/pdf']
  })
  async uploadFile(req, reply) {
    const file = req.file; // UploadedFile object
    const formData = req.body; // Other form fields

    console.log('File info:', {
      filename: file.filename,
      size: file.size,
      mimetype: file.mimetype
    });

    // Process file.buffer
    return { message: 'File uploaded successfully', filename: file.filename };
  }
}

Multiple Files Upload:

class FileController {
  @Post('/upload-multiple')
  @MultipleFiles({
    maxFiles: 5,
    maxFileSize: 2 * 1024 * 1024, // 2MB per file
    allowedMimeTypes: ['image/*']
  })
  async uploadMultiple(req, reply) {
    const files = req.files; // Array of UploadedFile
    const formData = req.body; // Other form fields

    return {
      message: `${files.length} files uploaded`,
      files: files.map(f => ({ name: f.filename, size: f.size }))
    };
  }
}

Headers

@Header(key, value)                    // Single header
@Headers({ key1: value1, key2: value2 }) // Multiple headers

Example:

@Headers({
  'X-API-Version': '1.0',
  'Cache-Control': 'no-cache'
})
class APIController {
  @Get('/data')
  @Header('Content-Type', 'application/json')
  async getData() {
    return { data: 'example' };
  }
}

Global Handlers (Middleware)

Global handlers execute before route handlers and can be used for logging, authentication, rate limiting, etc.

import { registerGlobalHandler, removeGlobalHandler } from './src/lib/framework';

// Logging middleware
registerGlobalHandler('logger', {
  handler: async (req, reply) => {
    console.log(`${req.method} ${req.url}`);
    return true; // Continue to next handler
  },
  priority: 100 // Higher priority executes first
});

// Rate limiting middleware
registerGlobalHandler('rateLimit', {
  handler: async (req, reply) => {
    // Rate limiting logic
    const isAllowed = checkRateLimit(req.ip);
    if (!isAllowed) {
      reply.status(429).send({ error: 'Rate limit exceeded' });
      return false; // Stop execution
    }
    return true;
  },
  methods: ['POST', 'PUT', 'DELETE'], // Only for these methods
  skipRoutes: ['/health', '/status'] // Skip these routes
});

// Remove handler when needed
removeGlobalHandler('rateLimit');

Advanced Examples

Complete CRUD Controller

import {
  Get, Post, Put, Delete, Body, Query, Params,
  AuthGuard, UseGuard, Header
} from './src/lib/framework';
import { z } from 'zod';

const UserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().min(18).optional()
});

const UpdateUserSchema = UserSchema.partial();

const UserParamsSchema = z.object({
  id: z.string().uuid()
});

const QueryParamsSchema = z.object({
  page: z.string().transform(Number).default('1'),
  limit: z.string().transform(Number).default('10'),
  search: z.string().optional()
});

@AuthGuard()
@Header('X-Service', 'UserService')
class UserController {
  @Get('/users')
  @Query(QueryParamsSchema)
  async getUsers(req, reply) {
    const { page, limit, search } = req.query;
    // Fetch users with pagination and search
    return {
      users: [],
      pagination: { page, limit, total: 0 }
    };
  }

  @Get('/users/:id')
  async getUser(@Params(UserParamsSchema) params: any) {
    const { id } = params;
    // Fetch user by ID
    return { user: { id, name: 'John Doe' } };
  }

  @Post('/users')
  @Body(UserSchema)
  async createUser(req, reply) {
    const userData = req.body;
    // Create new user
    reply.status(201);
    return { user: { id: 'new-id', ...userData } };
  }

  @Put('/users/:id')
  async updateUser(
    @Params(UserParamsSchema) params: any,
    @Body(UpdateUserSchema) body: any
  ) {
    const { id } = params;
    const updates = body;
    // Update user
    return { user: { id, ...updates } };
  }

  @Delete('/users/:id')
  @UseGuard(adminGuard)
  async deleteUser(@Params(UserParamsSchema) params: any, reply) {
    const { id } = params;
    // Delete user (admin only)
    reply.status(204);
    return;
  }
}

Error Handling

The framework provides built-in error responses:

import { createErrorResponse } from './src/lib/framework';

class UserController {
  @Get('/users/:id')
  async getUser(req, reply) {
    const user = await findUser(req.params.id);

    if (!user) {
      reply.status(404).send(
        createErrorResponse('User not found')
      );
      return;
    }

    return { user };
  }
}

Multiple Controllers Registration

import { registerControllers } from './src/lib/framework';

const app = Fastify();

registerControllers(app, [
  { controller: UserController, prefix: '/api/v1' },
  { controller: ProductController, prefix: '/api/v1' },
  { controller: FileController, prefix: '/api/v1' }
]);

TypeScript Support

The framework is fully typed with TypeScript. All decorators and types are exported:

import type {
  RouteHandler,
  GuardFunction,
  FileUploadConfig,
  UploadedFile,
  GlobalHandlerConfig
} from './src/lib/framework';

Best Practices

  1. Use Zod schemas for all validation to ensure type safety
  2. Implement proper error handling in route handlers
  3. Use guards for authentication and authorization
  4. Leverage global handlers for cross-cutting concerns
  5. Keep controllers focused on a single resource or domain
  6. Use meaningful HTTP status codes in responses

This framework provides a robust foundation for building scalable REST APIs with TypeScript, combining the performance of Fastify with the developer experience of decorators and strong typing.