Dependency Injection container for functional programming in Typescript and Express

625 views Asked by At

Is there any dependency injection container like InversifyJs but for functional programming in typescript? What I want to achieve is to inject a fake function from my tests end to end in the same way that i do with the OOP like the following example

Test end to end binding fake user repository

import magicContainer from 'magic-library'
import { UserRepositoryInterface } from '..'
import { FakeUserRepository } from '..'

describe(‘My test describe’, () => {

    test('My test', async () => {
        magicContainer.bind(UserRepositoryInterface, FakeUserRepository)

        await supertest(app)
        .post('/my-endpoint')
        .send({payload})
        .expect(HttpStatus.OK)
})

})

Express Router:

import {UserRepositoryInterface} from '...'
import {UserRepository} from '...'
import magicContainer from ‘magic-library’

const router = Router()
router.post('/my-endpoint', magicContainer.bind(UserRepositoryInterface, UserRepository)
)

User Interface and implementations:

export interface UserRepositoryInterface {
    myFunction: () => Promise<User[]>
}


export const UserRepository: UserRepositoryInterface = {
    myFunction: async () => {
        …
    }
}

export const FakeUserRepository: UserRepositoryInterface = {
    myFunction: async () => {
        …
    }
}

I have tried with libraries like InversifyJS, tsyringe and node-dependency-injection but they are purely object oriented, not for functional programming

1

There are 1 answers

0
tarek salem On

I faced the same problem before, some people argue that dependency injection is a design pattern that should be implemented in OOP only however this is not right, dependency injection and dependency inversion are principles can be applied on both functional programming and OOP The idea is very simple but first we should know the main components for dependency injection:

  • dependency
  • dependency consumer
  • DC/dependency container Regarding the dependency consumer, in OOP you have a constructor that you inject dependencies in however in FP you don't have that so you just need to inject a function inside a function, how can you do that? Thanks to javascript closures and higher order functions, using them you can implement a higher order function that receives the dependencies you need and returns a function that holds the concrete implementation so basically the higher-order function plays the constructor role. The third thing you need is the dependency injection container and this can be done using some data structure and algorithm to hold all your dependencies and pick the right one when it's required. I created a library for that purpose you can check it, I am using it currently on production and works very well https://github.com/tareksalem/injectx

Example:

1- create a dependency:

export class DB {
  users = [
    {
      username: "John",
    }
  ]
}
export const db = new DB();

2- Create a Dependency Consumer

export const GetUserRepository = ({ db }: { db: DB }) => (username: string) => {
    return db.users.find(user => user.username === username)
}
export const getUserReposistory = InjectIn(GetUserRepository)

3- bind the dependencies into the container

import { GetContainer } from "injectx"
import { db } from "./db"
import { GetUserRepository } from "./getUser.repository";

export const DiInit = () => {
  GetContainer('default').Bind(db, { name: 'db' }).Bind(GetUserRepository);
}