Ditox.js
    Preparing search index...

    Ditox.js

    Ditox.js

    lemon

    Dependency injection for web applications

    npm stars types license coverage

    Ditox.js is a lightweight dependency injection container for TypeScript. It provides a simple functional API to bind values and factories to container by tokens and resolve values later. The library supports different scopes for factory bindings, including "singleton", "scoped", and "transient". Bindings can be organized as a dependency module declaratively.

    Ditox.js works with containers, tokens, values, and value factories. There are no class decorators, field injectors, and other magic. Explicit binding and resolving are used.

    • Functional API
    • Container hierarchy
    • Scopes for factory bindings
    • Dependency modules
    • Multi-value tokens
    • TypeScript types

    The library is available as two packages:

    • ditox - DI container and core tools
    • ditox-react - Tools for React.js applications

    Please see the documentation at ditox.js.org

    You can use the following command to install packages:

    npm install --save ditox
    npm install --save ditox-react

    Packages can be used as UMD modules. Use jsdelivr.com CDN site to load ditox and ditox-react:


    <script src="//cdn.jsdelivr.net/npm/ditox/dist/umd/index.js"></script>
    <script src="//cdn.jsdelivr.net/npm/ditox-react/dist/umd/index.js"></script>
    <script>
    const container = Ditox.createContainer();
    // DitoxReact.useDependency(SOME_TOKEN);
    </script>
    • Token specifies a future injection of an "internal" implementation with a concrete "public" type.

      type Logger = (message: string) => void;

      const LOGGER_TOKEN = token<Logger>();
    • Container keeps bindings of tokens to concrete values and implementations

      const container = createContainer();
      container.bindValue(LOGGER_TOKEN, (message) => console.log(message));
    • Code graph is constructed at runtime by resolving values of tokens.

      const logger = container.resolve(LOGGER_TOKEN);
      logger('Hello World!');

    Create an injection token for a logger and DI container. Bind a logger implementation and resolve its value later in the application:

    import { createContainer, token } from 'ditox';

    type LoggerService = {
    log: (...messages: string[]) => void;
    };

    // Injection token
    const LOGGER_TOKEN = token<LoggerService>();

    // Default implementation
    const CONSOLE_LOGGER: LoggerService = {
    log: (...messages) => console.log(...messages),
    };

    // Create a DI container
    const container = createContainer();

    container.bindValue(LOGGER_TOKEN, CONSOLE_LOGGER);

    // Later, somewhere in the app
    const logger = container.resolve(LOGGER_TOKEN);
    logger.log('Hello World!');

    Bind a factory of a remote logger which depends on an HTTP client:

    import { injectable } from 'ditox';

    export type ServerClient = {
    log: (...messages: string[]) => void;
    sendMetric: (key: string, value: string) => void;
    };

    export const SERVER_CLIENT_TOKEN = token<ServerClient>();

    function createLoggerClient(client: ServerClient): Logger {
    return {
    log: (...messages) => client.log(...messages),
    };
    }

    container.bindFactory(
    LOGGER_TOKEN,
    injectable(createLoggerClient, SERVER_CLIENT_TOKEN),
    );

    // Later, somewhere in the app
    const logger = container.resolve(LOGGER_TOKEN);
    logger.log('Hello World!');

    Organize related bindings and functionality as a DI module:

    import { bindModule, declareModule } from 'ditox';

    type SendMetricFn = (key: string, value: string) => void;

    const SEND_METRIC_TOKEN = token<SendMetricFn>();

    // Declare a DI module
    const TELEMETRY_MODULE = declareModule<LoggerModule>({
    factory: injectable((client) => {
    const logger = createLoggerClient(client);

    const sendMetric = (key: string, value: string) => {
    logger.log('metric', key, value);
    client.sendMetric(key, value);
    };

    return { logger, sendMetric };
    }, SERVER_CLIENT_TOKEN),
    exports: {
    logger: LOGGER_TOKEN,
    sendMetric: SEND_METRIC_TOKEN,
    },
    });

    // Bind the module
    bindModule(container, TELEMETRY_MODULE);

    // Later, somewhere in the app
    const logger = container.resolve(LOGGER_TOKEN);
    logger.log('Hello World!');

    const sendMetric = container.resolve(SEND_METRIC_TOKEN);
    sendMetric('foo', 'bar');

    Wrap a component tree by a DI container and bind modules:

    // index.tsx

    import ReactDOM from 'react-dom';

    import { Greeting } from './Greeting';
    import { TELEMETRY_MODULE } from './telemetry';

    const APP_MODULE = declareModule({
    imports: [TELEMETRY_MODULE],
    });

    const App: FC = () => {
    return (
    <DependencyContainer root>
    <DependencyModule module={APP_MODULE}>
    <Greeting />
    </DependencyModule>
    </DependencyContainer>
    );
    };

    ReactDOM.render(<App />, document.getElementById('root'));

    Injecting a dependency by a React component:

    // Greeting.tsx

    import { useDependency } from 'ditox-react';

    export const Greeting: FC = () => {
    const logger = useDependency(LOGGER_TOKEN);

    useEffect(() => {
    logger.log('Hello World!');
    }, [logger]);

    return <>Hello</>;
    };
    • Follow 👨🏻‍💻 @mnasyrov on GitHub for announcements
    • Create a 💬 GitHub issue for bug reports, feature requests, or questions
    • Add a ⭐️ star on GitHub and 🐦 tweet to promote the project

    This project is licensed under the MIT license.