Brandi Dependency Injection

website·Indexed 30 days ago

https://brandi.js.org

A framework-agnostic dependency injection container powered by TypeScript. Brandi provides tools for managing dependencies through containers, binding types, and dependency modules, with a dedicated React bindings layer (Brandi-React) featuring ContainerProvider, useInjection, and createInjectionHooks.

Tokens
6.9K
Snippets
37
Records
51
Agent score
56%

What's inside Brandi

  1. Overview of Brandi Dependency Injection Container

    Brandi is a lightweight, strongly typed dependency injection (DI) container for TypeScript. It is framework-agnostic, meaning it can be used with any UI or server framework. Unlike many other DI containers, Brandi is decorator-free, requiring no special tsconfig.json parameters or Reflect polyfills.
  2. Overview of Brandi library features

    Brandi is a lightweight, high-performance dependency injection library with the following characteristics:

    • Framework Agnostic: Compatible with any UI or server-side framework.
    • Strongly Typed: Provides native TypeScript support out of the box.
    • Decorators Free: Does not require the Reflect polyfill or specific experimental flags/parameters in tsconfig.json to function.
  3. Organize bindings using DependencyModule

    A DependencyModule acts as a logical space to organize bindings. Unlike a Container, a DependencyModule can only store dependencies; it cannot resolve or inject them. It is used to group related bindings together, which can then be loaded into a Container using the .use().from() pattern.
  4. Initialize a Brandi Container

    The Container class is used to store information about dependencies, resolve them, and inject them into targets. You can instantiate a container using the new Container() constructor or the createContainer() alias.
    import { Container, createContainer } from 'brandi';
    
    const container1 = new Container();
    const container2 = createContainer();
  5. Understand Brandi's approach to Dependency Injection

    Brandi implements the Dependency Injection (DI) design pattern to achieve Inversion of Control (IoC) by moving the creation and binding of dependent objects outside of the classes that depend on them. Brandi specifically performs Constructor Injection and does not support other DI types.
  6. Configure a Container to use a DependencyModule

    To resolve dependencies defined within a DependencyModule, use the container.use(token).from(module) method. If a module contains a chain of dependencies, the container will automatically resolve the nested dependencies from the module without requiring each individual token to be bound to the container explicitly.
    import { Container, DependencyModule, token } from 'brandi';
    
    const TOKENS = { apiKey: token<string>('apiKey') };
    
    // 1. Create and configure the module
    const apiModule = new DependencyModule();
    apiModule.bind(TOKENS.apiKey).toConstant('#key9428');
    
    // 2. Tell the container to use the token from the module
    const container = new Container();
    container.use(TOKENS.apiKey).from(apiModule);
    
    const key = container.get(TOKENS.apiKey);
  7. Attach tags to components using the tagged HoC

    The tagged(...tags)(Component, [options]) Higher-Order Component (HoC) attaches specific tags to a React component and its child components. This is used in conjunction with brandi's conditional bindings to inject different dependencies based on the component's tags. By default, wrapped components and their children inherit tags from upstream tagged components. This inheritance can be disabled using the isolated option.
    import { tagged } from 'brandi-react';
    import { tag } from 'brandi';
    
    // Define a tag
    export const TAGS = { offline: tag('offline') };
    
    // Create a reusable HoC for this tag
    export const withOfflineTag = tagged(TAGS.offline);
    
    // Use the HoC to wrap a component
    export const SettingsComponent = withOfflineTag(() => {
      // This component and its children now have the 'offline' tag
      return <div>Settings</div>;
    });
  8. Install Brandi using npm or Yarn

    Brandi is available as a package for use with a module bundler or in a Node application. It is precompiled to ES2018 (CommonJS and ESModule) and provides ESNext builds via esnext, esnext:main, and esnext:module fields.
    # Using npm
    npm install brandi
    
    # Using Yarn
    yarn add brandi
  9. Implement hierarchical dependency resolution with Container.extend

    Brandi supports hierarchical DI systems where a container can be created as a child of another. When childContainer.get(token) is called, the system attempts to resolve the dependency in the following order:

    1. The child container's own bindings.
    2. The parent container's bindings.
    3. The parent's parent, continuing up the ancestor chain until the dependency is satisfied or the root is reached.
    import { Container, token } from 'brandi';
    
    class ApiService {}
    
    const TOKENS = {
      apiService: token<ApiService>('apiService'),
    };
    
    // 1. Create and configure the parent container
    const parentContainer = new Container();
    parentContainer
      .bind(TOKENS.apiService)
      .toInstance(ApiService)
      .inTransientScope();
    
    // 2. Create a child container by extending the parent
    const childContainer = new Container().extend(parentContainer);
    
    // 3. Resolve dependency from child; it bubbles up to parentContainer
    const apiService = childContainer.get(TOKENS.apiService);
    
    expect(apiService).toBeInstanceOf(ApiService);
  10. Declare and inject optional dependencies in Brandi

    To create an optional dependency in Brandi, use the standard TypeScript optional parameter syntax (?) in the class constructor. When registering the dependency with injected, append .optional to the token. If the optional dependency is not bound in the container, Brandi will return undefined instead of throwing an error.
    import { injected } from 'brandi';
    import { TOKENS } from './tokens';
    import { Logger } from './Logger';
    
    export class ApiService {
      // Declare as an optional dependency using TypeScript syntax
      constructor(private logger?: Logger) {}
    }
    
    // Inject using the .optional token field
    injected(
      ApiService,
      TOKENS.logger.optional
    );
  11. Install Brandi-React

    Brandi-React is the React bindings layer for Brandi, allowing React components to retrieve dependencies from Brandi containers. It requires React 16.8 or later and requires the core Brandi library to be installed.
    # Using NPM
    npm install brandi-react
    
    # Using Yarn
    yarn add brandi-react