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

@currentjs/router

v0.1.3

Published

Lightweight decorator-based HTTP/HTTPS server with routing,JWT authentication, and static files serving for @currentjs generated apps.

Downloads

140

Readme

@currentjs/router 🚀

A surprisingly capable HTTP router designed to be the backbone of the currentjs framework ecosystem. Mostly vibe-coded with Claude 4 Sonnet, because... why not?

What Is This Thing? 🤔

This is a decorator-based HTTP/HTTPS server that can handle APIs, serve static files, render templates, and even pretend to be a SPA framework. It's designed to be the routing backbone for apps generated by the currentjs framework and works best within that ecosystem.

Key Philosophy: Zero external dependencies, maximum vibes. We only use Node.js built-ins because we're rebels like that.

Installation 📦

Using @currentjs/gen (Code Generator) - Recommended

# No manual installation needed 
# The router is automatically included when you generate a project
currentjs create app my-app

(for more details see the documentation)

Manual Installation - For standalone use

npm i @currentjs/router

💡 Pro Tip: If you're using the currentjs code generator, you probably don't need to install this manually. The code generator (@currentjs/gen) will handle all the setup for you automatically!

Features That Actually Work ✨

  • 🎭 Decorators: @Get, @Post, @Put, @Patch, @Delete, @Render, @Controller
  • 🔀 Dynamic routing: Path parameters like /users/:id (because who has time to hardcode everything?)
  • 🔐 JWT Authentication: Built-in JWT parsing and user context (HS256, because we're not animals)
  • 📁 Static file serving: With path traversal protection (security matters, kids)
  • 🎨 Template rendering: SSR support with layouts and partial content for SPAs
  • 🔒 HTTPS support: Because it's 2025 and HTTP is so last decade
  • ⚡ Zero dependencies: Just Node.js and good vibes

Quick Start (The "I Just Want It To Work" Guide) 🏃‍♂️

import { Get, Post, Controller, createWebServer } from '@currentjs/router';
import type { IContext } from '@currentjs/router';

@Controller('/api/posts')
class PostController {
  @Get('/:id')
  async getPost(ctx: IContext) {
    const id = ctx.request.parameters.id;
    return { id, title: 'Hello World', author: 'Claude' };
  }

  @Post('/')
  async createPost(ctx: IContext) {
    // Access the authenticated user (if JWT token provided)
    const user = ctx.request.user;
    return { 
      ok: true, 
      received: ctx.request.body,
      createdBy: user?.email || 'anonymous'
    };
  }
}

const server = createWebServer({
  controllers: [new PostController()],
  webDir: './public' // Optional: serve static files too
});

server.listen(3000);
console.log('🚀 Server running on http://localhost:3000');

Decorators (The Magic Spells) 🪄

HTTP Method Decorators

@Get('/path')           // GET requests
@Post('/path')          // POST requests  
@Put('/path')           // PUT requests
@Patch('/path')         // PATCH requests
@Delete('/path')        // DELETE requests

Controller Decorator

@Controller('/api/v1')  // Base path for all routes in this controller
class MyController {
  @Get('/hello')        // Becomes GET /api/v1/hello
  sayHello() { return { message: 'Hi!' }; }
}

Render Decorator (For the Template Lovers)

@Controller('/')
class PageController {
  @Get('/dashboard')
  @Render('dashboard.html', 'layout.html')  // template, optional layout
  async dashboard(ctx: IContext) {
    return { 
      title: 'Dashboard',
      user: ctx.request.user,
      data: await getSomeData()
    };
  }
}

Path Parameters (Because Dynamic Is Better) 🛣️

@Get('/users/:userId/posts/:postId')
async getUserPost(ctx: IContext) {
  const { userId, postId } = ctx.request.parameters;
  // Query params are also in parameters!
  const { limit, offset } = ctx.request.parameters;
  return { userId, postId, limit, offset };
}

Authentication (JWT Magic) 🔐

Just include a JWT token in the Authorization header and we'll parse it for you:

// Request header: Authorization: Bearer your.jwt.token
@Get('/profile')
async getProfile(ctx: IContext) {
  const user = ctx.request.user; // Parsed from JWT
  if (!user) {
    throw new Error('Authentication required');
  }
  return { 
    id: user.id, 
    email: user.email, 
    role: user.role 
  };
}

JWT Requirements:

  • Algorithm: HS256
  • Secret: Set JWT_SECRET environment variable
  • Standard claims: id (or sub), role, email

Template Rendering (SSR in 2025? Bold!) 🎨

When you provide a renderer function, routes with @Render will return HTML:

import { createWebServer } from '@currentjs/router';
import { TemplateEngine } from '@currentjs/templating'; // Recommended!

const templateEngine = new TemplateEngine('./templates');

const server = createWebServer({
  controllers: [new PageController()],
  webDir: './public'
}, {
  renderer: async (template, data, layout) => {
    const content = await templateEngine.render(template, data);
    if (layout) {
      return await templateEngine.render(layout, { ...data, content });
    }
    return content;
  },
  errorTemplate: 'error.html' // Optional error page template
});

Pro Tip: While you can use any template engine, this router is optimized for @currentjs/templating which provides powerful features like component composition, conditional rendering, and seamless SPA support.

SPA Support: The router detects X-Partial-Content: true headers and skips the layout for seamless SPA navigation.

Static File Serving (Because Someone Has to Serve Those Cat GIFs) 📁

const server = createWebServer({
  controllers: [new ApiController()],
  webDir: './public'  // Serves static files from this directory
}, {
  indexFiles: ['index.html', 'index.htm']  // Default files for directories
});

Security Features:

  • Path traversal protection (no ../../../etc/passwd nonsense)
  • Proper MIME type detection
  • Directory index file serving

HTTPS (Because Security) 🔒

import fs from 'fs';

const server = createWebServer({
  controllers: [new SecureController()]
}, {
  https: {
    key: fs.readFileSync('./server.key'),
    cert: fs.readFileSync('./server.crt')
  }
});

server.listen(3443); // Traditional HTTPS port

Context Object (Your Window to the Request) 📦

Every route handler receives a context object with everything you need:

interface IContext {
  request: {
    url: string;                              // Original URL
    path: string;                             // Normalized path
    method: string;                           // HTTP method
    parameters: Record<string, string | number>; // Path params + query params
    body: any;                                // Parsed JSON or raw string
    headers: Record<string, string | string[]>; // Request headers
    user?: AuthenticatedUser;                 // Parsed JWT user (if authenticated)
  };
  response: Record<string, any>;              // For future response customization
}

Return Values (What You Give Is What You Get) 📤

  • Objects/Arrays: Automatically serialized to JSON with application/json content type
  • Strings: Sent as text/plain; charset=utf-8
  • Template routes: Rendered as HTML when using @Render decorator

Advanced Configuration 🔧

const server = createWebServer({
  controllers: [controller1, controller2],
  webDir: './public'
}, {
  port: 3000,
  host: '0.0.0.0',
  https: false, // or { key: Buffer, cert: Buffer }
  renderer: myTemplateRenderer,
  staticDir: './assets', // Override webDir for static files
  indexFiles: ['index.html', 'home.html'],
  errorTemplate: 'error.html'
});

Part of a Bigger Picture 🌍

This router is designed as the HTTP layer for the currentjs code generation framework. It's built to work seamlessly with generated controllers, services, domain models, and especially with @currentjs/templating for rendering. Think of it as the networking hub that connects all the generated pieces together.

Vibe Engineering 🎵

This package was primarily developed through conversations with Claude 4 Sonnet, which explains why the code feels like it was written by someone who actually enjoys programming. The result is a router that's both functional and surprisingly pleasant to use.

Authorship & contribution

Vibecoded with claude-4-sonnet (mostly) by Konstantin Zavalny. Yes, it is a vibecoded solution, really.

Any contributions such as bugfixes, improvements, etc are very welcome.

License

GNU Lesser General Public License (LGPL)

It simply means, that you:

  • can create a proprietary application that uses this library without having to open source their entire application code (this is the "lesser" aspect of LGPL compared to GPL).
  • can make any modifications, but must distribute those modifications under the LGPL (or a compatible license) and include the original copyright and license notice.