Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
latest
technanimals/asgardlightweight, type-safe service discovery library for TypeScript applications.
This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers




JSR Score
82%
Published
2 weeks ago (0.0.5)
@asgard/hermod
A TypeScript service discovery library for your application architecture.
Overview
@asgard/hermod
is a lightweight, type-safe service discovery library for TypeScript applications. Named after the Norse god who served as the messenger between the realms, this package facilitates communication between different services in your application, making it simple to register, discover, and consume services with full type safety.
Installation
# Using npm npm install @asgard/hermod # Using pnpm pnpm add @asgard/hermod # Using yarn yarn add @asgard/hermod # Using JSR npx jsr add @asgard/hermod
Key Features
- 🔍 Type-safe service discovery - Get full TypeScript type checking when accessing services
- 🧩 Singleton pattern - Avoid multiple service discovery instances with built-in singleton support
- 🔄 Async service registration - Support for both synchronous and asynchronous service initialization
- 🔌 Pluggable architecture - Create and register custom services easily
- 🛠️ Generic type parameters - Customize service names and instance types
Basic Usage
import { HermodServiceDiscovery, HermodService } from '@asgard/hermod'; // Define your service types interface Services { logger: Logger; database: Database; } // Create a service discovery instance const serviceDiscovery = HermodServiceDiscovery.getInstance<Services>(); // Create and register services class LoggerService extends HermodService<'logger', Logger> { serviceName = 'logger' as const; register() { return new Logger(); } } // Register the service new LoggerService(serviceDiscovery).discover(); // Access the service elsewhere in your code const logger = await serviceDiscovery.get('logger');
Examples
Creating Multiple Services
// Define service interfaces interface Logger { log(message: string): void; } interface Database { query(sql: string): Promise<unknown[]>; } // Define service types interface Services { logger: Logger; database: Database; } // Get service discovery instance const serviceDiscovery = HermodServiceDiscovery.getInstance<Services>(); // Create logger service class LoggerService extends HermodService<'logger', Logger> { serviceName = 'logger' as const; register() { return { log: (message: string) => console.log(`[LOG]: ${message}`) }; } } // Create database service class DatabaseService extends HermodService<'database', Database> { serviceName = 'database' as const; async register() { // Simulate async initialization await new Promise(resolve => setTimeout(resolve, 100)); return { query: async (sql: string) => { console.log(`Executing query: ${sql}`); return []; } }; } } // Register services new LoggerService(serviceDiscovery).discover(); new DatabaseService(serviceDiscovery).discover(); // Use services together async function main() { // Get individual services const logger = await serviceDiscovery.get('logger'); logger.log('Application started'); // Get multiple services at once const { logger: log, database } = await serviceDiscovery.getMany(['logger', 'database']); log.log('Querying database...'); const results = await database.query('SELECT * FROM users'); }
Service Dependencies
class ConfigService extends HermodService<'config', Record<string, any>> { serviceName = 'config' as const; register() { return { apiUrl: 'https://api.example.com', timeout: 5000 }; } } class ApiService extends HermodService<'api', ApiClient> { serviceName = 'api' as const; async register() { // Get config service const config = await this.serviceDiscovery.get('config'); // Use config to set up API client return new ApiClient(config.apiUrl, config.timeout); } } // Register in correct order new ConfigService(serviceDiscovery).discover(); new ApiService(serviceDiscovery).discover();
Best Practices
- Define service types upfront - Create an interface that maps service names to their instance types
- Use const assertions for service names ('serviceName' as const)
- Register services early in your application lifecycle
- Handle service dependencies carefully to avoid circular dependencies
- Use error handling when retrieving services that might not be registered
Checking Service Availability
The has
method allows you to check if a service exists before attempting to retrieve it:
// Check if a service exists if (serviceDiscovery.has('logger')) { // Service exists, safe to get const logger = await serviceDiscovery.get('logger'); logger.log('System started'); } else { // Handle missing service console.warn('Logger service not available, using console'); console.log('System started'); } // Using with optional services async function getAnalytics() { // Analytics might be optional if (serviceDiscovery.has('analytics')) { const analytics = await serviceDiscovery.get('analytics'); return analytics.getStats(); } // Return default empty data if service isn't available return { pageViews: 0, visitors: 0 }; } // Checking multiple services const requiredServices = ['database', 'auth', 'cache']; const missingServices = requiredServices.filter( service => !serviceDiscovery.has(service) ); if (missingServices.length > 0) { console.error(`Missing required services: ${missingServices.join(', ')}`); } else { // All services available, safe to start application startApp(); }
Built and signed on
GitHub Actions
Add Package
deno add jsr:@asgard/hermod
Import symbol
import * as hermod from "@asgard/hermod";
Import directly with a jsr specifier
import * as hermod from "jsr:@asgard/hermod";
Add Package
pnpm i jsr:@asgard/hermod
pnpm dlx jsr add @asgard/hermod
Import symbol
import * as hermod from "@asgard/hermod";
Add Package
yarn add jsr:@asgard/hermod
yarn dlx jsr add @asgard/hermod
Import symbol
import * as hermod from "@asgard/hermod";
Add Package
npx jsr add @asgard/hermod
Import symbol
import * as hermod from "@asgard/hermod";
Add Package
bunx jsr add @asgard/hermod
Import symbol
import * as hermod from "@asgard/hermod";