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

@periakteon/dunamisjs

v1.1.1

Published

Minimalist framework for creating a HTTP server

Readme

@periakteon/dunamisjs

A lightweight, decorator-based routing framework for Express.js. This library provides a modern, TypeScript-first approach to defining Express routes using class-based controllers and method decorators.

Note: This framework is currently under development. Documentation will be continuously improved as the project evolves.

Features

  • Class-based Controllers - Organize your routes using TypeScript classes
  • Decorator-based Routing - Define routes with simple, expressive decorators
  • Parameter Injection - Easily access request data with parameter decorators
  • Type Safety - Leverage TypeScript's type system for robust API development
  • Express Integration - Built on top of Express.js for reliability and ecosystem compatibility
  • Middleware Support - Apply middleware at controller or method level
  • Request Validation - Seamless integration with Zod for request validation
  • Error Handling - Centralized error handling with decorators
  • Minimalist Design - Clean API with minimal boilerplate code

Installation

npm install @periakteon/dunamisjs@latest [email protected] reflect-metadata zod
npm install ts-node -D @types/[email protected] @types/node typescript

You'll also need to configure TypeScript to support decorators. Add the following to your tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

Create a package.json file with the following:

{
  "name": "dunamis-test",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "dev": "node --loader ts-node/esm --watch src/index.ts",
    "build": "tsc --outDir dist",
    "start": "node dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@periakteon/dunamisjs": "^1.0.1",
    "express": "^4.21.2",
    "reflect-metadata": "^0.2.2",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.2"
  }
}

Run the following command to install the dependencies:

npm install

Run the following command to start the server:

npm run dev

Run the following command to build the project:

npm run build

Run the following command to start the server:

npm run start

Basic Usage

import "reflect-metadata";
import { createExpressApp, JSONController, Get, Post, Body, Param } from "@periakteon/dunamisjs";

@JSONController("/users")
class UserController {
  @Get()
  getAll() {
    return { users: [] }; // Returns as JSON automatically
  }

  @Get("/:id")
  getOne(@Param("id") id: string) {
    return { id, name: "Example User" };
  }

  @Post()
  create(@Body() userData: any) {
    return { ...userData, id: "123" };
  }
}

// Create Express app with controllers
const app = createExpressApp({
  controllers: [UserController],
});

// Start server
app.listen(3000, () => {
  console.log("Server is running on http://localhost:3000");
});

Detailed Documentation

Controllers

Controllers are classes decorated with @JSONController() that group related route handlers together.

import { JSONController } from "@periakteon/dunamisjs";

@JSONController("/api/products")
class ProductController {
  // Route handlers go here
}

The decorator takes an optional base path as its argument. All routes defined in the controller will be prefixed with this path.

HTTP Method Decorators

Use method decorators to define routes and their HTTP methods:

import { JSONController, Get, Post, Put, Delete, Patch } from "@periakteon/dunamisjs";

@JSONController("/api/products")
class ProductController {
  @Get() // Maps to GET /api/products
  getAllProducts() {
    // Logic to get all products
    return { products: [] };
  }

  @Get("/:id") // Maps to GET /api/products/:id
  getProduct(@Param("id") id: string) {
    // Logic to get a specific product
    return { id, name: "Product Name" };
  }

  @Post() // Maps to POST /api/products
  createProduct(@Body() productData: any) {
    // Logic to create a product
    return { id: "123", ...productData };
  }

  @Put("/:id") // Maps to PUT /api/products/:id
  updateProduct(@Param("id") id: string, @Body() productData: any) {
    // Logic to update a product
    return { id, ...productData, updated: true };
  }

  @Delete("/:id") // Maps to DELETE /api/products/:id
  deleteProduct(@Param("id") id: string) {
    // Logic to delete a product
    return { id, deleted: true };
  }

  @Patch("/:id") // Maps to PATCH /api/products/:id
  partialUpdateProduct(@Param("id") id: string, @Body() partialData: any) {
    // Logic for partial update
    return { id, ...partialData, patched: true };
  }
}

Parameter Decorators

Parameter decorators allow you to extract data from the request and inject it into your route handlers:

import {
  JSONController,
  Get,
  Post,
  Param,
  Query,
  Body,
  Req,
  Res,
  Headers,
} from "@periakteon/dunamisjs";
import { Request, Response } from "express";

@JSONController("/api")
class ExampleController {
  @Get("/items/:id")
  getItem(
    @Param("id") id: string, // URL parameter
    @Query("fields") fields?: string, // Query parameter (?fields=...)
    @Headers("authorization") auth?: string // Request header
  ) {
    return { id, fields, authProvided: !!auth };
  }

  @Post("/items")
  createItem(@Body() itemData: any) {
    // Request body
    return { created: true, item: itemData };
  }

  @Get("/advanced")
  advancedExample(
    @Req() req: Request, // Express Request object
    @Res() res: Response // Express Response object
  ) {
    // When using @Res(), you should handle the response manually
    res.status(200).json({ message: "Using raw Express objects" });
  }
}

Available parameter decorators:

  • @Param(name): URL parameters
  • @Query(name): Query string parameters
  • @Body(): Request body
  • @Headers(name): Request headers
  • @Req(): Express Request object
  • @Res(): Express Response object

Middleware

Middleware can be applied at both controller and method levels using the @UseMiddleware decorator:

import { JSONController, Get, UseMiddleware } from "@periakteon/dunamisjs";
import { Request, Response, NextFunction } from "express";

// Define middleware functions
function loggingMiddleware(req: Request, res: Response, next: NextFunction) {
  console.log(`Request received: ${req.method} ${req.url}`);
  next();
}

function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  // Validate token logic would go here
  next();
}

// Apply middleware to controller or methods
@JSONController("/api/secure")
@UseMiddleware(loggingMiddleware) // Applied to all routes in this controller
class SecureController {
  @Get("/public")
  publicRoute() {
    return { message: "This route only has logging middleware" };
  }

  @Get("/private")
  @UseMiddleware(authMiddleware) // Applied only to this method
  privateRoute() {
    return { message: "This route has both logging and auth middleware" };
  }

  // You can also apply multiple middleware functions at once
  @Get("/admin")
  @UseMiddleware([
    authMiddleware,
    (req, res, next) => {
      // Check if user is admin
      if (req.headers["user-role"] !== "admin") {
        return res.status(403).json({ message: "Forbidden" });
      }
      next();
    },
  ])
  adminRoute() {
    return { message: "Admin-only route" };
  }
}

You can also apply middleware when creating controllers:

@JSONController({
  prefix: "/api/protected",
  middleware: [authMiddleware],
})
class ProtectedController {
  // All routes will have the authMiddleware applied
}

And when defining routes:

@Get({
  path: "/logs",
  middleware: [loggingMiddleware]
})
getLogs() {
  return { logs: [] };
}

Additionally, you can apply global middleware when creating your Express app:

const app = createExpressApp({
  controllers: [UserController],
  globalMiddleware: [
    (req, res, next) => {
      console.log(`${req.method} ${req.path}`);
      next();
    },
  ],
});

Request Validation with Zod

Dunamis.js integrates with Zod for request validation:

import { JSONController, Post, Body } from "@periakteon/dunamisjs";
import { z } from "zod";

// Define Zod schema for validation
const UserSchema = z.object({
  username: z.string().min(3).max(20),
  email: z.string().email(),
  age: z.number().min(18).optional(),
});

// Infer TypeScript type from Zod schema
type User = z.infer<typeof UserSchema>;

@JSONController("/api/users")
class UserController {
  @Post()
  @ValidateBody(UserSchema) // Validate request body against schema
  createUser(@Body() userData: User) {
    // Type-safe access to validated data
    // If validation fails, an error is automatically thrown
    // and the handler is not executed
    return {
      message: "User created successfully",
      user: userData,
    };
  }
}

When validation fails, a 400 Bad Request response is sent with details about the validation errors.

Error Handling

Use the @ErrorHandler() decorator to implement centralized error handling for controllers:

import { JSONController, Get, ErrorHandler, HttpError } from "@periakteon/dunamisjs";

@JSONController("/api/example")
@ErrorHandler() // Enable error handling for this controller
class ExampleController {
  @Get("/safe")
  safeRoute() {
    return { message: "This works fine" };
  }

  @Get("/error")
  errorRoute() {
    // This error will be caught and formatted as JSON
    throw new HttpError(404, "Resource not found");
  }

  @Get("/unexpected")
  unexpectedError() {
    // Even unexpected errors are handled
    throw new Error("Something went wrong");
  }

  // Optional: Custom error handler method
  handleError(error: Error) {
    if (error instanceof HttpError) {
      return {
        message: error.message,
        status: error.statusCode,
      };
    }

    // Handle unexpected errors
    console.error("Unexpected error:", error);
    return {
      message: "Internal server error",
      status: 500,
    };
  }
}

The error response will be automatically formatted as JSON with the following structure:

{
  "message": "Error message",
  "status": 404
}

Complete Example

Here's a more complete example showing multiple features together:

import "reflect-metadata";
import {
  createExpressApp,
  JSONController,
  Get,
  Post,
  Body,
  Param,
  Query,
  UseMiddleware,
  ErrorHandler,
  HttpError,
  ValidateBody,
} from "@periakteon/dunamisjs";
import { z } from "zod";
import { Request, Response, NextFunction } from "express";

// Auth middleware
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ message: "Unauthorized" });
  }
  next();
};

// Validation schema
const ProductSchema = z.object({
  name: z.string().min(2),
  price: z.number().positive(),
  description: z.string().optional(),
});

type Product = z.infer<typeof ProductSchema>;

@JSONController("/api/products")
@UseMiddleware(authMiddleware) // Apply auth to all routes
@ErrorHandler() // Enable error handling
class ProductController {
  // In-memory product store for demo
  private products: Record<string, Product> = {};
  private nextId = 1;

  @Get()
  getAllProducts(@Query("sort") sort?: string) {
    let productList = Object.entries(this.products).map(([id, data]) => ({
      id,
      ...data,
    }));

    if (sort === "price") {
      productList = productList.sort((a, b) => a.price - b.price);
    }

    return { products: productList };
  }

  @Get("/:id")
  getProduct(@Param("id") id: string) {
    const product = this.products[id];
    if (!product) {
      throw new HttpError(404, `Product with ID ${id} not found`);
    }
    return { id, ...product };
  }

  @Post()
  @ValidateBody(ProductSchema)
  createProduct(@Body() productData: Product) {
    const id = String(this.nextId++);
    this.products[id] = productData;
    return { id, ...productData };
  }
}

// Create and start app
const app = createExpressApp({
  controllers: [ProductController],
  // Optional global middleware
  globalMiddleware: [
    (req, res, next) => {
      console.log(`${req.method} ${req.path}`);
      next();
    },
  ],
});

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

API Reference

For a complete list of all decorators and functions, see the following sections:

Core Functions

  • createExpressApp(options): Creates and configures an Express application

Application Configuration

The createExpressApp function accepts a configuration object with the following options:

  • controllers (required): Array of controller classes to register with the application

    controllers: [UserController, ProductController];
  • routePrefix (optional): Global route prefix applied to all controllers

    routePrefix: "/api/v1";
  • globalMiddleware (optional): Array of middleware functions applied to all routes

    globalMiddleware: [loggingMiddleware, authMiddleware];
  • cors (optional): CORS middleware configuration

    • true: Enables CORS with default settings
    • object: Enables CORS with custom options
    • false or omitted: CORS is disabled
    // Default configuration
    cors: true
    
    // Custom configuration
    cors: {
      origin: ['https://example.com', 'https://dev.example.com'],
      methods: ['GET', 'POST', 'PUT', 'DELETE'],
      credentials: true
    }
  • helmet (optional): Helmet middleware for security headers

    • true: Enables Helmet with default settings
    • object: Enables Helmet with custom options
    • false or omitted: Helmet is disabled
    // Default configuration
    helmet: true
    
    // Custom configuration
    helmet: {
      contentSecurityPolicy: false,
      xssFilter: true
    }
  • bodyParser (optional, defaults to true): Body parsing middleware configuration

    • true: Enables body parsing with default settings (JSON and URL-encoded)
    • object: Custom configuration for JSON and URL-encoded parsers
    • false: Body parsing is disabled
    // Default configuration
    bodyParser: true
    
    // Custom configuration
    bodyParser: {
      json: { limit: '10mb', strict: true },
      urlencoded: { extended: true, limit: '10mb' }
    }
  • logger (optional): Request logging middleware (requires 'morgan' package)

    • true: Enables logging with default format ('dev')
    • string: Uses the specified format string
    • object: Passes custom options to morgan
    • false or omitted: Logging is disabled
    // Default format ('dev')
    logger: true
    
    // Custom format
    logger: 'combined'
    
    // Custom options
    logger: {
      format: 'combined',
      skip: (req, res) => res.statusCode < 400
    }
  • errorHandler (optional): Global error handling middleware

    • true: Enables the default error handler
    • false or omitted: No error handler is applied
    errorHandler: true;

Example configuration:

const app = createExpressApp({
  controllers: [UserController, ProductController],
  routePrefix: "/api/v1",
  cors: {
    origin: "*",
    methods: ["GET", "POST", "PUT", "DELETE"],
  },
  helmet: true,
  bodyParser: {
    json: { limit: "1mb" },
    urlencoded: { extended: true },
  },
  logger: "dev",
  errorHandler: true,
  globalMiddleware: [
    (req, res, next) => {
      console.log(`Request: ${req.method} ${req.path}`);
      next();
    },
  ],
});

Controller Decorators

  • @JSONController(basePath?): Creates a controller with JSON response type

Method Decorators

  • @Get(path?): Maps a method to GET requests
  • @Post(path?): Maps a method to POST requests
  • @Put(path?): Maps a method to PUT requests
  • @Delete(path?): Maps a method to DELETE requests
  • @Patch(path?): Maps a method to PATCH requests

Parameter Decorators

  • @Param(name): Injects URL parameters
  • @Query(name): Injects query parameters
  • @Body(): Injects request body
  • @Headers(name): Injects request headers
  • @Req(): Injects Express Request object
  • @Res(): Injects Express Response object

Middleware Decorators

  • @UseMiddleware(middleware): Applies middleware to a controller or method

Validation Decorators

  • @ValidateBody(schema): Validates request body using Zod schema
  • @ValidateParams(schema): Validates URL parameters using Zod schema
  • @ValidateQuery(schema): Validates query parameters using Zod schema

Error Handling

  • @ErrorHandler(): Enables error handling for a controller
  • HttpError: Custom error class for HTTP errors

Contributing

Contributions are welcome! Please feel free to submit pull requests.

Release Process

This project uses semantic-release for automatic versioning and package publishing. The release process is triggered automatically when commits are pushed to the main branch.

Commit Message Format

We follow the Conventional Commits specification which is used by semantic-release to determine the next version number and generate changelogs. Your commit messages should be structured as follows:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Types that trigger releases:

  • fix: - represents bug fixes (correlates to PATCH in SemVer)
  • feat: - represents a new feature (correlates to MINOR in SemVer)
  • feat!:, fix!:, refactor!:, etc. - represents breaking changes (correlates to MAJOR in SemVer)

Examples:

  • fix(router): fix route parameter extraction
  • feat(decorators): add new @Session() decorator
  • feat!: redesign middleware API

Other types that don't trigger releases:

  • docs: - documentation changes
  • style: - changes that don't affect code functionality (formatting, etc.)
  • refactor: - code changes that neither fix bugs nor add features
  • test: - adding or correcting tests
  • chore: - maintenance tasks

Please refer to the semantic-release documentation for more information on the release process.

License

MIT