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

@ooneex/app

v1.16.1

Published

Full-featured application framework for Bun — orchestrates routing, middleware pipelines, dependency injection, caching, logging, and WebSocket support in a modular architecture

Readme

@ooneex/app

Full-featured application framework for Bun — orchestrates routing, middleware pipelines, dependency injection, caching, logging, and WebSocket support in a modular architecture.

Bun TypeScript MIT License

Features

HTTP Routing - Decorator-based route definitions with parameter validation

WebSocket Support - Real-time bidirectional communication with pub/sub

Middleware Pipeline - Request/response processing with custom middleware

Caching Integration - Built-in cache support for Redis and filesystem

Database Support - TypeORM integration for PostgreSQL and SQLite

File Storage - Storage abstraction for local, Cloudflare R2, and Bunny CDN

Logging - Structured logging with terminal and SQLite outputs

Cron Jobs - Scheduled task execution with timezone support

Analytics - PostHog integration for user behavior tracking

Email Services - Nodemailer and Resend integration for transactional emails

Static Files - Serve static assets

SPA Support - Single-page application routing support

Installation

bun add @ooneex/app

Usage

Basic Application Setup

import { App } from '@ooneex/app';
import { AppEnv } from '@ooneex/app-env';
import { TerminalLogger } from '@ooneex/logger';
import { UserModule } from './modules/UserModule';

const app = new App({
  modules: [UserModule],
  loggers: [TerminalLogger],
  env: new AppEnv('development')
});

await app.run();

With Full Configuration

import { App } from '@ooneex/app';
import { AppEnv } from '@ooneex/app-env';
import { TerminalLogger, SqliteLogger } from '@ooneex/logger';
import { RedisCache } from '@ooneex/cache';
import { PostHogAnalytics } from '@ooneex/analytics';
import { CloudflareStorage } from '@ooneex/storage';
import { TypeormPgDatabase } from '@ooneex/database';
import { ResendMailerAdapter } from '@ooneex/mailer';
import { AuthMiddleware } from './middleware/AuthMiddleware';
import { CleanupCron } from './cron/CleanupCron';
import { UserModule, ProductModule } from './modules';

const app = new App({
  modules: [UserModule, ProductModule],
  loggers: [TerminalLogger, SqliteLogger],
  analytics: PostHogAnalytics,
  cache: RedisCache,
  storage: CloudflareStorage,
  database: new TypeormPgDatabase(),
  mailer: ResendMailerAdapter,
  cronJobs: [CleanupCron],
  middlewares: [AuthMiddleware],
  env: new AppEnv(process.env.APP_ENV as 'development')
});

await app.run();

Environment Variables

# Required
APP_ENV=development
PORT=3000
HOST_NAME=0.0.0.0

# Optional (based on services used)
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_key
CACHE_REDIS_URL=redis://localhost:6379
DATABASE_URL=postgresql://user:pass@localhost:5432/db
ANALYTICS_POSTHOG_PROJECT_TOKEN=your_posthog_key

Creating a Module

import { Route } from '@ooneex/routing';
import { UserController } from './UserController';
import { UserEntity } from './UserEntity';
import type { ModuleType } from '@ooneex/module';

export const UserModule: ModuleType = {
  controllers: [UserController],
  entities: [UserEntity],
  middlewares: [],
  cronJobs: [],
  events: [],
};

Creating a Controller

import { Route } from '@ooneex/routing';
import type { IController, ContextType } from '@ooneex/controller';
import type { IResponse } from '@ooneex/http-response';

@Route.http({
  name: 'api.users.list',
  path: '/api/users',
  method: 'GET',
  description: 'List all users'
})
class UserListController implements IController {
  public async index(context: ContextType): Promise<IResponse> {
    const users = await context.database?.open(UserEntity);
    
    return context.response.json({
      users: users || []
    });
  }
}

WebSocket Routes

import { Route } from '@ooneex/routing';
import type { IController, ContextType } from '@ooneex/socket';

@Route.socket({
  name: 'api.chat.connect',
  path: '/ws/chat',
  description: 'Real-time chat connection'
})
class ChatController implements IController {
  public async index(context: ContextType): Promise<IResponse> {
    await context.channel.subscribe();
    
    return context.response.json({
      connected: true
    });
  }
}

API Reference

Classes

App

Main application class that bootstraps and runs your web application.

Constructor:

new App(config: AppConfigType)

Methods:

init(): Promise<App>

Initializes the application, validates environment variables, and prepares services.

Returns: Promise resolving to the App instance

run(): Promise<App>

Starts the HTTP server and begins listening for requests.

Returns: Promise resolving to the App instance

Example:

const app = new App(config);
await app.init();
await app.run();

Types

AppConfigType

type AppConfigType = {
  modules: ModuleType[];
  loggers: LoggerClassType[];
  analytics?: AnalyticsClassType;
  cache?: CacheClassType;
  storage?: StorageClassType;
  mailer?: MailerClassType;
  rateLimiter?: RateLimiterClassType;
  cronJobs?: CronClassType[];
  database?: IDatabase | ITypeormDatabase;
  env: IAppEnv;
  spa?: Bun.HTMLBundle;
  middlewares?: MiddlewareClassType[] | SocketMiddlewareClassType[];
};

Advanced Usage

Single Page Application (SPA) Mode

import { App } from '@ooneex/app';

const app = new App({
  // ... other config
  spa: Bun.html('./dist/index.html')
});

await app.run();

Custom Middleware

import { decorator } from '@ooneex/middleware';
import type { IMiddleware, ContextType } from '@ooneex/middleware';

@decorator.middleware()
class AuthMiddleware implements IMiddleware {
  public async handle(context: ContextType): Promise<ContextType> {
    const token = context.header.get('Authorization');
    
    if (!token) {
      context.response.exception('Unauthorized', { status: 401 });
      return context;
    }
    
    // Validate token and set user
    context.user = await this.validateToken(token);
    
    return context;
  }
}

Cron Jobs

import { Cron, type CronTimeType } from '@ooneex/cron';
import type { TimeZoneType } from '@ooneex/country';

class CleanupCron extends Cron {
  public getTime(): CronTimeType {
    return 'every 1 hours';
  }
  
  public getTimeZone(): TimeZoneType | null {
    return 'Europe/Paris';
  }
  
  public async job(): Promise<void> {
    // Cleanup logic here
    console.log('Running cleanup...');
  }
}

Accessing Services in Controllers

@Route.http({
  name: 'api.products.create',
  path: '/api/products',
  method: 'POST',
  description: 'Create a new product'
})
class ProductCreateController implements IController {
  public async index(context: ContextType): Promise<IResponse> {
    const { logger, cache, storage, database, mailer, analytics } = context;
    
    // Use logger
    logger.info('Creating product', { userId: context.user?.id });
    
    // Use cache
    await cache?.set('product:latest', context.payload);
    
    // Use storage
    const file = context.files['image'];
    if (file) {
      await storage?.putFile('products/image.jpg', file.path);
    }
    
    // Use analytics
    analytics?.capture({
      id: context.user?.id || 'anonymous',
      event: 'product_created'
    });
    
    return context.response.json({ success: true });
  }
}

Error Handling

import { Exception } from '@ooneex/exception';
import { HttpStatus } from '@ooneex/http-status';

@Route.http({
  name: 'api.users.show',
  path: '/api/users/:id',
  method: 'GET',
  description: 'Get user by ID'
})
class UserShowController implements IController {
  public async index(context: ContextType): Promise<IResponse> {
    const { id } = context.params;
    
    const user = await this.userRepository.findById(id);
    
    if (!user) {
      return context.response.notFound('User not found', {
        data: { id }
      });
    }
    
    return context.response.json({ user });
  }
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

  1. Clone the repository
  2. Install dependencies: bun install
  3. Run tests: bun run test
  4. Build the project: bun run build

Guidelines

  • Write tests for new features
  • Follow the existing code style
  • Update documentation for API changes
  • Ensure all tests pass before submitting PR

Made with ❤️ by the Ooneex team