Socket
Book a DemoInstallSign in
Socket

@analog-tools/inject

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@analog-tools/inject

Dependency injection for AnalogJS server-side applications

0.0.9
latest
Source
npmnpm
Version published
Weekly downloads
11
-45%
Maintainers
1
Weekly downloads
 
Created
Source

@analog-tools/inject

⚠️ IMPORTANT: Early Development Stage ⚠️
This project is in its early development stage. Breaking changes may happen frequently as the APIs evolve. Use with caution in production environments.

A lightweight yet powerful dependency injection system for H3-based server applications (Nitro, AnalogJS), providing a clean, type-safe approach to managing services and dependencies.

npm version License: MIT

Features

  • 🧩 Simple, lightweight dependency injection system
  • 🔍 Full TypeScript support with type safety
  • 📦 Singleton pattern implementation for services
  • 🔄 Automatic lazy initialization of services
  • 🛠️ Support for service registration with constructor parameters
  • 🧪 Easy to test with custom service implementations and mocks
  • 🔁 Complete test lifecycle support with registry reset functions

Installation

# Using npm
npm install @analog-tools/inject

# Using pnpm
pnpm add @analog-tools/inject

# Using yarn
yarn add @analog-tools/inject

Quick Start

Here's a basic example of creating injectable services:

import { inject, registerService } from '@analog-tools/inject';

// Define a service class with the INJECTABLE static property
class LoggerService {
  static INJECTABLE = true;

  log(message: string): void {
    console.log(`[Logger] ${message}`);
  }
}

// Define another service that depends on LoggerService
class UserService {
  static INJECTABLE = true;

  private logger = inject(LoggerService);

  getUserInfo(userId: string) {
    this.logger.log(`Getting info for user: ${userId}`);
    // Implementation...
    return { id: userId, name: 'Example User' };
  }
}

// Usage in your application
const userService = inject(UserService);
const userInfo = userService.getUserInfo('user123');

Usage with AnalogJS

In API Routes

// src/server/routes/api/users/[id].ts
import { defineEventHandler } from 'h3';
import { inject } from '@analog-tools/inject';
import { UserService } from '../../../services/user.service';

export default defineEventHandler(async (event) => {
  const userService = inject(UserService);
  const userId = event.context.params.id;

  const userData = await userService.getUserById(userId);

  return userData;
});

Creating Services

// src/server/services/user.service.ts
import { inject } from '@analog-tools/inject';
import { DatabaseService } from './database.service';

export class UserService {
  static INJECTABLE = true;

  private db = inject(DatabaseService);

  async getUserById(id: string) {
    return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
  }

  async createUser(userData: any) {
    // Implementation...
  }
}

// src/server/services/database.service.ts
export class DatabaseService {
  static INJECTABLE = true;

  private connection: any;

  constructor() {
    // Initialize database connection
    this.connection = {}; // Example placeholder
  }

  async query(sql: string, params: any[] = []) {
    // Implementation...
    return [];
  }
}

Advanced Usage

Using Constructor Parameters

You can pass constructor parameters when registering services:

import { registerService } from '@analog-tools/inject';

class ConfigService {
  static INJECTABLE = true;

  constructor(private readonly apiUrl: string) {}

  getApiUrl() {
    return this.apiUrl;
  }
}

// Register with constructor parameters
registerService(ConfigService, 'https://api.example.com');

// Now use it anywhere
const configService = inject(ConfigService);
const apiUrl = configService.getApiUrl(); // Returns 'https://api.example.com'

Optional Injection

If you want to handle cases where a service might not be available:

import { inject } from '@analog-tools/inject';

const analytics = inject(AnalyticsService, { required: false });
if (analytics) {
  analytics.trackEvent('page_view');
}

API Reference

inject<T>(token: InjectionServiceClass<T>, options?: InjectOptions): T

The primary method to retrieve a service instance:

  • token: The class/constructor function of the service to inject
  • options:
    • required (default: true): Whether to throw an error if the service doesn't exist

registerService<T>(token: InjectionServiceClass<T>, ...properties: any[]): void

Registers a service with optional constructor parameters:

  • token: The class/constructor function of the service to register
  • properties: Any typesafe constructor parameters the service requires

registerServiceAsUndefined<T>(token: InjectionServiceClass<T>): void

Registers a service as undefined in the registry:

  • token: The class/constructor function of the service to register

Useful for testing scenarios where you want to verify correct handling of missing services.

registerMockService<T>(token: InjectionServiceClass<T>, customObject: Partial<T>): void

Registers a custom implementation of a service for testing:

  • token: The class/constructor function of the service to register
  • customObject: A custom object that will be used as the service instance

resetAllInjections(): void

Clears all registered services from the registry. Useful for testing to ensure a clean state between tests.

Making a Class Injectable

For a class to be injectable, it needs the INJECTABLE static property:

class MyService {
  static INJECTABLE = true;

  // Service implementation...
}

TypeScript Type Safety

The registerCustomServiceInstance function accepts Partial<T> objects, making it easy to create partial implementations with only the methods you need:

interface Logger {
  info(message: string): void;
  error(message: string, error?: Error): void;
  debug(message: string): void;
}

class LoggerService implements Logger {
  static INJECTABLE = true;
  info(message: string): void { /* ... */ }
  error(message: string, error?: Error): void { /* ... */ }
  debug(message: string): void { /* ... */ }
}

// Only need to implement the methods you use in your tests
registerMockService(LoggerService, {
  info: vi.fn(),
  error: vi.fn()
  // debug is optional since we're using Partial<T>
});

Testing with @analog-tools/inject

The @analog-tools/inject package provides several utilities to make testing easier:

Mocking Services with Custom Implementations

import { registerMockService, resetAllInjections } from '@analog-tools/inject';
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';

// Service we want to test
class UserService {
  static INJECTABLE = true;
  private logger = inject(LoggerService);
  
  createUser(userData: any) {
    this.logger.info(`Creating user: ${userData.name}`);
    // Implementation...
  }
}

// Test suite
describe('UserService', () => {
  // Mock logger for testing
  const mockLogger = {
    info: vi.fn(),
    error: vi.fn()
  };
  
  beforeEach(() => {
    // Register mock implementation before each test
    registerMockService(LoggerService, mockLogger);
  });
  
  afterEach(() => {
    // Clean up after each test
    resetAllInjections();
    vi.clearAllMocks();
  });
  
  it('should log user creation', () => {
    const userService = inject(UserService);
    userService.createUser({ name: 'Test User' });
    
    expect(mockLogger.info).toHaveBeenCalledWith('Creating user: Test User');
  });
});

Best Practices

  • Keep services focused: Each service should have a single responsibility
  • Use dependency injection: Instead of creating service instances directly
  • Add proper typing: Leverage TypeScript's type system for better safety
  • Unit test services: Use dependency injection to mock service dependencies
  • Clean test state: Use resetAllInjections() in test teardown to ensure tests don't affect each other

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

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

Keywords

analogjs

FAQs

Package last updated on 21 Aug 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.