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

@atlex/core

v0.1.11

Published

IoC container, routing, middleware, events, and HTTP foundation for Atlex

Downloads

1,272

Readme

@atlex/core

A robust, feature-rich framework core for building scalable Node.js applications

npm TypeScript

Buy Me A Coffee

Installation

npm install @atlex/core
# or
yarn add @atlex/core

Quick Start

import { Application, Injectable, Controller, Get, Route } from '@atlex/core'

// Define a service
@Injectable()
export class UserService {
  getUser(id: number) {
    return { id, name: 'John Doe' }
  }
}

// Define a controller
@Controller()
export class UserController {
  constructor(private userService: UserService) {}

  @Get('/users/:id')
  getUser(request) {
    const user = this.userService.getUser(request.params.id)
    return { data: user }
  }
}

// Bootstrap your application
const app = new Application()

// Register service providers
app.register(new UserServiceProvider())

// Register routes
Route.get('/health', async (request, response) => {
  return { status: 'healthy' }
})

// Start listening
app.listen(3000, () => {
  console.log('Server running on port 3000')
})

Features

  • Dependency Injection Container - Powerful IoC container with automatic resolution
  • Flexible Routing - Fluent API and decorator-based route definitions
  • Middleware Pipeline - Express-like middleware with request/response handling
  • Service Providers - Structured application bootstrap and configuration
  • Validation - Built-in request validation with customizable rules
  • Event System - Publish/subscribe event dispatcher with decorator support
  • Exception Handling - Centralized error handling and custom exceptions
  • Decorators - Clean decorator syntax for controllers, routes, and dependency injection
  • Request/Response Helpers - Convenient request/response access throughout application
  • WebSocket Server - Room-based broadcasting with JWT auth on upgrade (v0.1.7)

Dependency Injection

The core of @atlex/core is its powerful dependency injection container that automatically resolves dependencies.

Container Binding

import { Container } from '@atlex/core'

const container = new Container()

// Bind to a concrete instance
container.bind('config', { apiKey: 'secret' })

// Bind to a singleton (resolved once, reused)
container.singleton('database', Database)

// Bind with a factory function
container.bind('logger', (container) => {
  return new Logger(container.make('config'))
})

// Create an alias
container.alias('db', 'database')

// Make (resolve) from container
const db = container.make('database')

Dependency Injection Decorators

import { Injectable, Singleton, Inject } from '@atlex/core'

// Mark class as injectable (transient - new instance each time)
@Injectable()
export class EmailService {
  send(to: string, subject: string) {
    // Send email logic
  }
}

// Mark class as singleton (single instance throughout app)
@Singleton()
export class DatabaseConnection {
  private connection

  connect() {
    this.connection = this.createConnection()
  }
}

// Inject dependencies
@Injectable()
export class UserService {
  constructor(
    private emailService: EmailService,
    private db: DatabaseConnection,
    @Inject('config') private config: any,
  ) {}

  registerUser(email: string) {
    this.emailService.send(email, 'Welcome!')
    // Save to database
  }
}

Advanced Container Usage

// Contextual binding - different implementations for different classes
container.contextualRule(PaymentProcessor, 'gateway', (container) => container.make(StripeGateway))

container.contextualRule(ReportGenerator, 'gateway', (container) => container.make(PayPalGateway))

// Check if binding exists
if (container.has('logger')) {
  const logger = container.make('logger')
}

// Flush and reset container
container.flush()

Routing

Define routes using the fluent API or decorators for a clean, expressive syntax.

Fluent Route API

import { Route } from '@atlex/core'

// Basic routes
Route.get('/users', (request, response) => {
  return { users: [] }
})

Route.post('/users', (request, response) => {
  const user = request.body
  return { id: 1, ...user }
})

Route.put('/users/:id', (request, response) => {
  const { id } = request.params
  return { id, updated: true }
})

Route.patch('/users/:id', (request, response) => {
  return { patched: true }
})

Route.delete('/users/:id', (request, response) => {
  return { deleted: true }
})

// Route parameters and queries
Route.get('/users/:id', (request, response) => {
  const userId = request.params.id
  const filter = request.query.filter
  return { userId, filter }
})

// Wildcard routes
Route.get('/files/*', (request, response) => {
  return { path: request.params[0] }
})

// Named routes
Route.post('/users', (request, response) => {
  return { id: 1 }
}).name('users.store')

// Route groups
Route.group({ prefix: '/api/v1' }, () => {
  Route.get('/users', (request, response) => {
    return { users: [] }
  })

  Route.post('/users', (request, response) => {
    return { created: true }
  })
})

Route.group({ prefix: '/admin', middleware: ['auth', 'admin'] }, () => {
  Route.get('/dashboard', (request, response) => {
    return { data: 'admin' }
  })
})

Decorator-Based Routes

import { Controller, Get, Post, Put, Delete, Patch } from '@atlex/core'

@Controller('/api/v1/users')
export class UserController {
  constructor(private userService: UserService) {}

  @Get('/:id')
  show(request) {
    return { user: this.userService.getUser(request.params.id) }
  }

  @Post()
  store(request) {
    return { created: this.userService.create(request.body) }
  }

  @Put('/:id')
  update(request) {
    return { updated: this.userService.update(request.params.id, request.body) }
  }

  @Patch('/:id')
  patch(request) {
    return { patched: true }
  }

  @Delete('/:id')
  destroy(request) {
    return { deleted: true }
  }
}

// Register controller with application
app.resolveController(UserController)

Middleware

import { Route } from '@atlex/core'

// Define middleware
const authMiddleware = async (request, response, next) => {
  if (!request.headers.authorization) {
    return response.status(401).json({ error: 'Unauthorized' })
  }
  request.user = { id: 1, name: 'John' }
  return next()
}

const corsMiddleware = async (request, response, next) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  return next()
}

// Apply to routes
Route.get('/protected', authMiddleware, (request, response) => {
  return { user: request.user }
})

// Apply to groups
Route.group({ middleware: [corsMiddleware] }, () => {
  Route.get('/public', (request, response) => {
    return { public: true }
  })
})

// Apply globally
app.use(corsMiddleware)
app.use(authMiddleware)

Service Providers

Service providers are the central place of all Atlex application bootstrap. They register bindings, boot services, and handle configuration.

import { ServiceProvider } from '@atlex/core'

export class DatabaseServiceProvider extends ServiceProvider {
  // Register bindings into the container
  register() {
    this.app.container.singleton('database', () => {
      return new Database({
        host: process.env.DB_HOST,
        port: process.env.DB_PORT,
        username: process.env.DB_USER,
        password: process.env.DB_PASSWORD,
      })
    })
  }

  // Boot the service (called after all services are registered)
  boot() {
    const db = this.app.container.make('database')
    db.connect()
  }
}

export class CacheServiceProvider extends ServiceProvider {
  register() {
    this.app.container.singleton('cache', () => {
      return new CacheManager(this.app.container.make('config.cache'))
    })
  }

  boot() {
    const cache = this.app.container.make('cache')
    cache.warmup()
  }
}

// Register providers with application
const app = new Application()
app.register(new DatabaseServiceProvider())
app.register(new CacheServiceProvider())
app.boot()

Validation

Validate request data with a powerful, fluent validation API.

import { validate, Validator, ValidationException, MessageBag } from '@atlex/core'

// Quick validation
Route.post('/users', async (request, response) => {
  try {
    const data = await validate(request.body, {
      name: 'required|string|min:3',
      email: 'required|email|unique:users',
      age: 'integer|min:18',
    })

    return { user: data }
  } catch (error) {
    if (error instanceof ValidationException) {
      return response.status(422).json({ errors: error.messages })
    }
    throw error
  }
})

// Advanced validation
const validator = new Validator(request.body, {
  name: 'required|string|max:255',
  email: 'required|email',
  password: 'required|min:8|confirmed',
  website: 'url|nullable',
  role: 'in:admin,user,guest',
  permissions: 'array|min:1',
})

if (validator.fails()) {
  const errors = validator.getMessageBag()
  return response.status(422).json({ errors: errors.all() })
}

const validated = validator.validated()

// Custom validation rules
validator.addRule('even', (value) => {
  return value % 2 === 0
})

validator.addRule('custom-domain', (value) => {
  return value.endsWith('@company.com')
})

// Custom error messages
const validator2 = new Validator(
  request.body,
  {
    email: 'required|email',
  },
  {
    'email.required': 'An email address is required',
    'email.email': 'Please provide a valid email address',
  },
)

Events

Publish and subscribe to events throughout your application using a clean event system.

import { Event, EventDispatcher, Listen } from '@atlex/core'

// Define an event
export class UserRegistered extends Event {
  constructor(public user: User) {
    super()
  }
}

export class OrderCreated extends Event {
  constructor(public order: Order) {
    super()
  }
}

// Listen to events with decorators
@Injectable()
export class SendWelcomeEmail {
  @Listen(UserRegistered)
  handle(event: UserRegistered) {
    console.log(`Sending welcome email to ${event.user.email}`)
    // Send email logic
  }
}

@Injectable()
export class ProcessOrder {
  @Listen(OrderCreated)
  handle(event: OrderCreated) {
    console.log(`Processing order ${event.order.id}`)
    // Process order logic
  }
}

// Dispatch events
const dispatcher = app.container.make(EventDispatcher)
dispatcher.dispatch(new UserRegistered(user))
dispatcher.dispatch(new OrderCreated(order))

// Listen to events programmatically
dispatcher.listen(UserRegistered, (event: UserRegistered) => {
  console.log('User registered:', event.user)
})

dispatcher.listen(OrderCreated, (event: OrderCreated) => {
  console.log('Order created:', event.order)
})

Exception Handling

Handle exceptions globally with a centralized error handler.

import { Application, Exception } from '@atlex/core'

// Custom exception
export class AuthenticationException extends Exception {
  constructor(message = 'Unauthenticated') {
    super(message)
    this.status = 401
  }
}

export class AuthorizationException extends Exception {
  constructor(message = 'Forbidden') {
    super(message)
    this.status = 403
  }
}

// Global exception handler
app.onException((error, request, response) => {
  if (error instanceof ValidationException) {
    return response.status(422).json({
      message: 'Validation failed',
      errors: error.messages,
    })
  }

  if (error instanceof AuthenticationException) {
    return response.status(401).json({
      message: error.message,
    })
  }

  if (error instanceof AuthorizationException) {
    return response.status(403).json({
      message: error.message,
    })
  }

  // Default error response
  return response.status(500).json({
    message: 'Internal Server Error',
    error: process.env.NODE_ENV === 'development' ? error.message : undefined,
  })
})

// Throw exceptions in routes/controllers
@Controller('/admin')
export class AdminController {
  @Get('/dashboard')
  dashboard(request) {
    if (!request.user) {
      throw new AuthenticationException('You must be logged in')
    }

    if (request.user.role !== 'admin') {
      throw new AuthorizationException('You do not have permission to access this')
    }

    return { dashboard: 'data' }
  }
}

Request and Response Helpers

Access request and response objects with convenient global helpers.

import { request, response } from '@atlex/core'

// Access request properties
Route.post('/data', (req, res) => {
  const body = req.body
  const query = req.query
  const params = req.params
  const headers = req.headers
  const method = req.method
  const url = req.url

  // Check request method
  if (req.method === 'POST') {
    // Handle POST
  }

  // Get header value
  const contentType = req.header('Content-Type')
  const bearerToken = req.header('Authorization')

  // Get input from body or query
  const name = req.input('name')
  const page = req.input('page', 1) // with default

  // Check if input exists
  if (req.has('email')) {
    const email = req.input('email')
  }

  return { received: true }
})

// Response helpers
Route.get('/data', (req, res) => {
  // Send JSON
  res.json({ data: 'value' })

  // Send with status
  res.status(201).json({ created: true })

  // Set headers
  res.setHeader('X-Custom', 'value')
  res.setHeader('X-Total-Count', '100')

  // Redirect
  res.redirect('/home')
  res.redirect(301, '/new-location')

  // Download file
  res.download('/path/to/file', 'filename.pdf')

  // Send raw response
  res.send('Hello World')
  res.send(Buffer.from('binary data'))
})

Application Class

The main Application class orchestrates your entire application lifecycle.

import { Application, ServiceProvider } from '@atlex/core'

const app = new Application({
  basePath: __dirname,
  environment: 'production',
})

// Register service providers
app.register(new DatabaseServiceProvider())
app.register(new CacheServiceProvider())
app.register(new EventServiceProvider())

// Boot all providers
app.boot()

// Make instances from container
const userService = app.make(UserService)
const config = app.make('config')

// Get the container directly
const container = app.container

// Get environment
const env = app.environment // 'production'
const isDev = app.isDev() // false
const isProd = app.isProd() // true

// Listen on port
app.listen(3000, () => {
  console.log('Application running on port 3000')
})

// Resolve and boot controllers
app.resolveController(UserController)
app.resolveController(AdminController)

// Register global middleware
app.use((request, response, next) => {
  console.log(`${request.method} ${request.url}`)
  return next()
})

// Handle exceptions
app.onException((error, request, response) => {
  console.error(error)
  return response.status(500).json({ error: 'Server error' })
})

// Handle shutdown
process.on('SIGTERM', async () => {
  await app.shutdown()
  process.exit(0)
})

WebSocket Server

@atlex/core v0.1.7 ships a room-based WebSocket module with JWT auth on the HTTP upgrade handshake. Invalid or missing tokens are rejected with 401 before the WebSocket handshake completes.

Setup

Install the ws peer dependency if not already present:

npm install ws
npm install --save-dev @types/ws

Define a Gateway

Extend WsGateway to handle connections and implement room logic:

import { WsGateway, WsClient } from '@atlex/core'

// Augment WsClientMeta with your JWT claim shape
declare module '@atlex/core' {
  interface WsClientMeta {
    userId: string
    familyId: string
  }
}

export class AppWsGateway extends WsGateway {
  handleConnection(client: WsClient): void {
    // Subscribe the client to rooms on connect
    client.join(`family:${client.meta.familyId}`)
    client.join(`device:${client.meta.userId}`)
  }

  handleDisconnect(_client: WsClient): void {
    // Client is removed from all rooms automatically after this hook
  }
}

Register with the App

Pass WsServiceProvider a gateway class and a verifyToken callback before app.listen():

import { WsServiceProvider } from '@atlex/core'
import { AppWsGateway } from './AppWsGateway.js'

app.register(
  new WsServiceProvider({
    gateway: AppWsGateway,
    verifyToken: async (token) => {
      // Return decoded JWT claims — throw to reject the upgrade
      return app.make(JwtProvider).verify(token)
    },
  }),
)

app.boot().listen(3000)

The token is read from the Authorization: Bearer <token> header or the ?token= query parameter.

Broadcast from Anywhere

Resolve the gateway from the container and call broadcast:

const gw = app.make(AppWsGateway)

// Broadcast to everyone in a room
gw.broadcast('family:abc123', 'location:update', { lat: 40.7, lng: -74.0 })

// Send to a specific client
client.send('status:update', { online: true })

Room Management

WsRoom tracks which clients are in which rooms. client.join / client.leave are scoped to the current client — use them inside handleConnection:

handleConnection(client: WsClient): void {
  client.join('global')
  client.join(`family:${client.meta.familyId}`)
}

// Check room size (via the injected WsRoom)
const room = app.make<WsRoom>('ws.room')
console.log(room.size('family:abc123')) // number of connected clients

WebSocket API Reference

WsGateway

| Method / Property | Description | | --------------------------------- | ---------------------------------------- | | handleConnection(client) | Abstract — called on successful JWT auth | | handleDisconnect(client) | Abstract — called on socket close | | broadcast(room, event, payload) | Send JSON event to all clients in a room |

WsClient

| Method / Property | Description | | ---------------------- | ----------------------------------------- | | socket | Raw ws.WebSocket instance | | meta | Decoded JWT claims (WsClientMeta) | | rooms | Set<string> of current room memberships | | join(room) | Subscribe to a room (idempotent) | | leave(room) | Unsubscribe from a room | | send(event, payload) | Send a typed event to this client only |

WsServiceProvider options

| Option | Type | Description | | ------------- | ------------------------------------------ | --------------------------------- | | gateway | typeof WsGateway | Your gateway subclass to register | | verifyToken | (token: string) => Promise<WsClientMeta> | Token validator; throw to reject |

API Overview

Container

| Method | Description | | --------------------------------------------- | ---------------------------------------- | | bind(key, value \| factory) | Bind a value or factory to the container | | singleton(key, constructor \| factory) | Bind a singleton (single instance) | | instance(key, instance) | Register a pre-built instance | | alias(alias, key) | Create an alias for a binding | | make(key) | Resolve and return a binding | | contextualRule(concrete, abstract, factory) | Set contextual binding | | has(key) | Check if binding exists | | flush() | Clear all bindings |

Route

| Method | Description | | -------------------------- | ----------------------------------- | | get(path, handler) | Register GET route | | post(path, handler) | Register POST route | | put(path, handler) | Register PUT route | | patch(path, handler) | Register PATCH route | | delete(path, handler) | Register DELETE route | | group(options, callback) | Group routes with shared attributes | | middleware(names) | Apply middleware to route | | name(name) | Give route a name |

Decorators

| Decorator | Target | Purpose | | ---------------------- | --------- | ------------------------------ | | @Injectable() | Class | Mark as injectable (transient) | | @Singleton() | Class | Mark as singleton | | @Inject(key?) | Parameter | Inject dependency | | @Controller(prefix?) | Class | Define controller | | @Get(path?) | Method | GET route handler | | @Post(path?) | Method | POST route handler | | @Put(path?) | Method | PUT route handler | | @Patch(path?) | Method | PATCH route handler | | @Delete(path?) | Method | DELETE route handler | | @Listen(event) | Method | Listen to event | | @Middleware(names) | Method | Apply middleware to handler |

Validation

| Class/Function | Purpose | | ---------------------------------- | -------------------------------------- | | validate(data, rules, messages?) | Quick validation function | | Validator | Full validation class | | ValidationException | Exception thrown on validation failure | | MessageBag | Collection of validation messages |

Events

| Class | Purpose | | --------------------- | ----------------------------- | | Event | Base event class | | EventDispatcher | Central event dispatcher | | @Listen(eventClass) | Decorator to listen to events |

Configuration

Configure the application through environment variables and a configuration file.

// config/app.ts
export default {
  name: 'Atlex Application',
  environment: process.env.NODE_ENV || 'development',
  debug: process.env.APP_DEBUG === 'true',
  key: process.env.APP_KEY,
}

// config/database.ts
export default {
  default: process.env.DB_CONNECTION || 'postgres',
  connections: {
    postgres: {
      driver: 'postgres',
      host: process.env.DB_HOST,
      port: process.env.DB_PORT,
      database: process.env.DB_NAME,
      username: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
    },
  },
}

// Access configuration
const app = new Application()
const appConfig = app.make('config.app')
const dbConfig = app.make('config.database')

Documentation

For detailed documentation, examples, and API reference, visit the Atlex documentation.

License

MIT

Part of Atlex — A modern framework for Node.js.