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

@alisdev/router-api-kit

v1.0.3

Published

Decorator-based controller library for Node.js APIs with Express/Fastify support, Zod validation, Swagger UI, and structured exception handling

Downloads

405

Readme

@alisdev/router-api-kit

A decorator-based controller library for Node.js APIs. Supports Express and Fastify, includes Zod-based request validation, structured exception handling, optional Swagger UI generation, and cookie management.

Installation

npm install @alisdev/router-api-kit reflect-metadata zod

Note: You also need one of the supported frameworks:

npm install express          # for Express
npm install fastify          # for Fastify

Requirements

Enable decorators in your tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true
  }
}

Import reflect-metadata at the top of your entry file:

import "reflect-metadata";

Quick Start

import "reflect-metadata";
import express from "express";
import { RouterKit, ReqController, GetMapping, PostMapping } from "@alisdev/router-api-kit";

@ReqController("/hello")
class HelloController {
  @GetMapping("/")
  async sayHello() {
    return { greeting: "Hello, World!" };
  }
}

const app = express();
app.use(express.json());

RouterKit.setup({
  framework: "express",
  app,
  swagger: { enabled: true, path: "/api/docs" },
});

RouterKit.register(HelloController);

app.listen(3000, () => console.log("Server running on port 3000"));

API Reference

RouterKit

Central setup class. Configured once at application entry point.

RouterKit.setup(config: RouterKitConfig): void
RouterKit.register(...controllers: Class[]): void
RouterKit.handleNotFound(): void

Configuration:

interface RouterKitConfig {
  framework: "express" | "fastify";
  app: express.Application | FastifyInstance;
  authMiddleware?: MiddlewareFn;       // for authentication: "auth"
  refreshMiddleware?: MiddlewareFn;    // for authentication: "refresh"
  swagger?: {
    enabled: boolean;
    path?: string;            // default: "/api/docs"
    title?: string;           // default: "API Documentation"
    version?: string;         // default: "1.0.0"
    sortBy?: "path" | "method";
    searchByPath?: boolean;
    searchByMethod?: boolean;
    searchByTag?: boolean;
  };
  logger?: boolean | {
    enabled?: boolean;
    handler?: (level: "info" | "error", message: string, meta: any) => void;
  };
}

@ReqController(basePath, options?)

Marks a class as a route controller with a base path and default authentication.

@ReqController("/users", { authentication: "auth", tag: "User Management" })
class UserController { ... }

Options: | Option | Type | Default | Description | |---|---|---|---| | authentication | "auth" \| "refresh" \| false | false | Default auth for all routes | | tag | string | Derived from class name | Swagger tag | | swagger | boolean | true | Include in Swagger docs |

Method Mapping Decorators

@GetMapping(path, options?)
@PostMapping(path, options?)
@PutMapping(path, options?)
@PatchMapping(path, options?)
@DeleteMapping(path, options?)

Options: | Option | Type | Description | |---|---|---| | authentication | "auth" \| "refresh" \| false | Override controller-level auth | | swagger | boolean | Include in Swagger docs | | tag | string | Override controller tag | | summary | string | Swagger summary | | description | string | Swagger description | | status | number | Default response status code |

Parameter Decorators

| Decorator | Description | |---|---| | @Body(schema?) | Injects and validates req.body with Zod | | @Query(schema?) | Injects and validates req.query with Zod | | @Param(key) | Injects a single route parameter | | @Req() | Injects the raw request object | | @Cookie() | Injects a CookieSetter instance |

Method Decorators

| Decorator | Description | |---|---| | @UseMiddleware(...fns) | Applies middleware to a route | | @HttpStatus(code) | Sets default response status code |

Exceptions

All exceptions extend BaseException and auto-send the appropriate HTTP response.

| Exception | Status | Code | |---|---|---| | BadRequestException | 400 | BAD_REQUEST | | UnauthorizedException | 401 | UNAUTHORIZED | | ForbiddenException | 403 | FORBIDDEN | | NotFoundException | 404 | NOT_FOUND | | ConflictException | 409 | CONFLICT | | ServerErrorException | 500 | INTERNAL_SERVER_ERROR |

CookieSetter

Injected via @Cookie(). Abstracts cookie operations across frameworks.

cookie.set(key, value, options?)   // Set a cookie
cookie.get(key)                    // Get a cookie value
cookie.delete(key)                 // Delete a cookie

Full Usage Example

import "reflect-metadata";
import express from "express";
import { z } from "zod";
import {
  RouterKit,
  ReqController,
  GetMapping, PostMapping, PutMapping, DeleteMapping,
  Body, Query, Param, Req, Cookie,
  UseMiddleware, HttpStatus,
  BadRequestException, NotFoundException, ConflictException,
  CookieSetter,
} from "@alisdev/router-api-kit";

// ── Zod Schemas ───────────────────────────────────────────────────

const CreateUserSchema = z.object({
  firstName: z.string().min(1),
  lastName: z.string().min(1),
  email: z.string().email(),
  age: z.coerce.number().min(1),
});

const UserQuerySchema = z.object({
  page: z.coerce.number().default(1),
  size: z.coerce.number().default(10),
  search: z.string().optional(),
});

const LoginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6),
});

type ICreateUser = z.infer<typeof CreateUserSchema>;
type IUserQuery = z.infer<typeof UserQuerySchema>;
type ILoginBody = z.infer<typeof LoginSchema>;

// ── Controllers ───────────────────────────────────────────────────

@ReqController("/auth", { tag: "Authentication", authentication: false })
class AuthController {
  @PostMapping("/login", { summary: "Login user" })
  async login(
    @Body(LoginSchema) body: ILoginBody,
    @Cookie() cookie: CookieSetter
  ) {
    // const { accessToken, refreshToken } = await AuthService.login(body);
    // cookie.set("refresh_token", refreshToken, { httpOnly: true, secure: true, maxAge: 7 });
    // return { accessToken };
  }

  @PostMapping("/refresh", { authentication: "refresh", summary: "Refresh access token" })
  async refresh(@Req() req: express.Request) {
    // const token = await AuthService.refresh(req);
    // return { accessToken: token };
  }

  @PostMapping("/logout")
  async logout(@Cookie() cookie: CookieSetter) {
    cookie.delete("refresh_token");
    return null;
  }
}

@ReqController("/users", { authentication: "auth", tag: "User Management" })
class UserController {
  @GetMapping("/", { summary: "Get all users with pagination" })
  async getAll(@Query(UserQuerySchema) query: IUserQuery) {
    // return await UserService.findAll(query);
  }

  @GetMapping("/:id", { summary: "Get user by ID" })
  async getById(@Param("id") id: string) {
    // const user = await UserService.findById(id);
    // if (!user) throw new NotFoundException("User not found");
    // return user;
  }

  @PostMapping("/", { authentication: false, summary: "Register new user" })
  @HttpStatus(201)
  async create(@Body(CreateUserSchema) body: ICreateUser) {
    // const exists = await UserService.findByEmail(body.email);
    // if (exists) throw new ConflictException("Email already registered");
    // return await UserService.create(body);
  }

  @PutMapping("/:id", { summary: "Update user" })
  async update(
    @Param("id") id: string,
    @Body(CreateUserSchema.partial()) body: Partial<ICreateUser>
  ) {
    // const user = await UserService.update(id, body);
    // if (!user) throw new NotFoundException("User not found");
    // return user;
  }

  @DeleteMapping("/:id", { summary: "Delete user" })
  async delete(@Param("id") id: string) {
    // const deleted = await UserService.delete(id);
    // if (!deleted) throw new NotFoundException("User not found");
    // return null;
  }
}

// ── App Setup ─────────────────────────────────────────────────────

const app = express();
app.use(express.json());

RouterKit.setup({
  framework: "express",
  app,
  // authMiddleware: verifyAccessToken,
  // refreshMiddleware: verifyRefreshToken,
  swagger: {
    enabled: true,
    path: "/api/docs",
    title: "My API",
    version: "1.0.0",
    sortBy: "path",
    searchByPath: true,
    searchByMethod: true,
    searchByTag: true,
  },
});

// Register multiple controllers at once
RouterKit.register(AuthController, UserController);

// Optionally handle 404 Not Found for unregistered routes
RouterKit.handleNotFound();

app.listen(3000, () => console.log("Server running on port 3000"));

Response Format

Success:

{
  "message": "OK",
  "data": { "_id": "...", "firstName": "Dudi" }
}

Error:

{
  "message": "User not found",
  "data": null
}

Validation Error:

{
  "message": "Validation failed",
  "data": {
    "errors": [
      { "field": "age", "message": "Expected number, received string" },
      { "field": "email", "message": "Invalid email format" }
    ]
  }
}

License

MIT