SkillAgentSearch skills...

Jimple

A lightweight dependency injection container for Node.js and browsers, built with modern ES6 features and with full support for TypeScript.

Install / Use

/learn @fjorgemota/Jimple
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Jimple

GitHub Actions Workflow Status npm version npm downloads node version JSDelivr

Jimple is a lightweight and powerful dependency injection container for JavaScript and TypeScript, built for Node.js and the browser. Inspired by Pimple, it brings clean, flexible dependency management to modern JavaScript projects — with zero dependencies and a minimal API.

Table of Contents

Features

Lightweight - ~1KB minified and gzipped
Zero dependencies - No external dependencies in Node.js
Universal - Works in Node.js and browsers
TypeScript - Fully typed with excellent IDE support
ES6 Proxy support - Modern syntax with property access
Extensible - Easy to extend and customize
Well tested - 100% code coverage
Stable API - Mature, stable API you can depend on

Why Dependency Injection?

Dependency injection helps you write more maintainable, testable code by:

  • Decoupling components - Services don't need to know how their dependencies are created
  • Improving testability - Easy to swap dependencies with mocks during testing
  • Managing complexity - Centralized configuration of how objects are wired together
  • Lazy loading - Services are only created when needed
  • Singleton by default: Same instance returned on subsequent calls
  • Dependency management: Services can depend on other services

Quick Start

npm install jimple@2.0.0-beta.6
import Jimple from "jimple";

// Create container
const container = new Jimple();

// Define a simple service
container.set("logger", (c) => {
  return {
    log: (msg) => console.log(`[${new Date().toISOString()}] ${msg}`),
  };
});

// Define a service that depends on another
container.set("userService", (c) => {
  const logger = c.get("logger");
  return {
    createUser: (name) => {
      logger.log(`Creating user: ${name}`);
      return { id: Math.random(), name };
    },
  };
});

// Use your services
const userService = container.get("userService");
const user = userService.createUser("Alice");

Installation

npm

npm install jimple

CDN (Browser)

<script src="https://cdn.jsdelivr.net/npm/jimple@2.0.0-beta.6/dist/Jimple.umd.js"></script>

Import Methods

ES6 Modules

import Jimple from "jimple";

CommonJS

const Jimple = require("jimple");

AMD

define(["jimple"], function (Jimple) {
  // Your code here
});

Core Concepts

Services

Services are objects that perform tasks in your application. They're defined as functions that return the service instance:

// Database connection service
container.set("database", (c) => {
  const config = c.get("dbConfig");
  return new Database(config.host, config.port);
});

// Email service that depends on database
container.set("emailService", (c) => {
  const db = c.get("database");
  return new EmailService(db);
});

Parameters

Parameters store configuration values, strings, numbers, or any non-function data:

// Configuration parameters
container.set("dbConfig", {
  host: "localhost",
  port: 5432,
  database: "myapp",
});

container.set("apiKey", "abc123");
container.set("isProduction", process.env.NODE_ENV === "production");

Factory Services

When you need a new instance every time instead of a singleton:

container.set(
  "httpRequest",
  container.factory((c) => {
    const config = c.get("httpConfig");
    return new HttpRequest(config);
  }),
);

// Each call returns a new instance
const req1 = container.get("httpRequest");
const req2 = container.get("httpRequest"); // Different instance

Advanced Features

Protecting Functions

To store an actual function (not a service factory) as a parameter:

container.set(
  "utility",
  container.protect(() => {
    return Math.random() * 100;
  }),
);

const utilityFn = container.get("utility"); // Returns the function itself
const result = utilityFn(); // Call the function

Extending Services

Add behavior to existing services:

container.set("logger", (c) => new Logger());

// Extend the logger to add file output
container.extend("logger", (logger, c) => {
  logger.addFileHandler("/var/log/app.log");
  return logger;
});

Removing Services or Parameters

Remove services or parameters from the container with unset():

container.set("logger", (c) => new Logger());
container.set("apiUrl", "https://api.example.com");

// Remove a service
container.unset("logger");
console.log(container.has("logger")); // false

// Remove a parameter
container.unset("apiUrl");
console.log(container.has("apiUrl")); // false

// Safe to unset non-existent services
container.unset("nonExistent"); // No error thrown

Important Notes:

  • Removes the service/parameter completely from the container
  • Clears any cached instances and metadata for services
  • Cannot be undone - you'll need to re-register the service
  • Safe to call on non-existent services (no error thrown)

Optional Dependencies & Defaults

Handle optional services with fallbacks:

container.set("cache", (c) => {
  if (c.has("redisConfig")) {
    return new RedisCache(c.get("redisConfig"));
  }
  return new MemoryCache(); // Fallback
});

Raw Service Access

Get the service definition function instead of the service itself:

container.set("database", (c) => new Database());

const dbFactory = container.raw("database");
const db1 = dbFactory(container);
const db2 = dbFactory(container); // Create another instance manually

ES6 Proxy Mode

Use modern JavaScript syntax for a more natural API:

const container = new Jimple();

// Set services using property syntax
container["logger"] = (c) => new Logger();
container["userService"] = (c) => new UserService(c["logger"]);

// Access services as properties
const userService = container.userService;

Limitations:

  • Can't overwrite built-in methods (set, get, etc.)
  • Accessing non-existent properties throws an error
  • TypeScript requires special handling (see below)

TypeScript Support

Jimple provides full TypeScript support with interface definitions:

Basic TypeScript Usage

interface Services {
  logger: Logger;
  database: Database;
  userService: UserService;
  apiKey: string;
}

const container = new Jimple<Services>();

container.set("apiKey", "secret-key");
container.set("logger", (c) => new Logger());
container.set("database", (c) => new Database());
container.set(
  "userService",
  (c) => new UserService(c.get("logger"), c.get("database")),
);

// Type-safe access
const userService: UserService = container.get("userService"); // ✅
const wrong: Database = container.get("userService"); // ❌ Compile error

TypeScript with Proxy Mode

interface Services {
  logger: Logger;
  userService: UserService;
}

const container = Jimple.create<Services>({
  logger: (c) => new Logger(),
  userService: (c) => new UserService(c.logger),
});

const userService: UserService = container.userService; // ✅ Type-safe

Note: Due to TypeScript limitations with proxies, you can't set properties directly. Use the set method instead:

container.set("newService", (c) => new Service()); // ✅ Works
container.newService = (c) => new Service(); // ❌ TypeScript error

Modular Configuration with Providers

Organize your container configuration into reusable modules:

Basic Provider

const databaseProvider = {
  register(container) {
    container.set("dbConfig", {
      host: process.env.DB_HOST ?? "localhost",
      port: process.env.DB_PORT ?? 5432,
    });

    container.set("database", (c) => {
      const config = c.get("dbConfig");
      return new Database(config);
    });
  },
};

container.register(databaseProvider);

File-based Providers (Node.js)

// providers/database.js
module.exports.register = function (container) {
  container.set("database", (c) => new Database(c.get("dbConfig")));
};

// main.js
container.register(require("./providers/database"));

Provider Helper

const { provider } = require("jimple");

module.exports = provider((container) => {
  container.set("apiService", (c) => new ApiService(c.get("apiConfig")));
});

Multiple Named Providers

module.exports = {
  database: provider((c) => {
    c.set("database", () => new Database());
  }),
  cache: provider((c) => {
    c.set("cache", () => new Cache());
  }),
};
View on GitHub
GitHub Stars79
CategoryCustomer
Updated1mo ago
Forks10

Languages

TypeScript

Security Score

100/100

Audited on Feb 20, 2026

No findings