@merkl/conduit

0.0.4 • Public • Published

conduit

An opiniated light-weight TypeScript code-splitting and dependency injection library for organizing your code.

Why ?

Conduit was built to handle code-splitting and dependency issues that arises when your codebase gets bigger. It provides a minimal set of functions to handle create modules, services, and dependencies fully typed and declarative. It allows for a cleaner import tree since all the dependencies are evaluated at runtime, your service only imports the types of other services. This approach is efficient for the frontend as speeds up hot module reloading, and for the backend as it unlocks all the feature that dependency injection provides when using database or cache clients, development setups etc...

How do I use it

Conduit is pretty flexible, you could just use it to declare dependency-inject bits of code that is modular:

//user.service.ts

export const UserService = createConduit<{ api: ApiClient; config: any, repository: }>((inject) => {

    const getUser = inject(["api"], ({api}, query: {id: string}) => {
        return api.users.find({where: {id}})
    })

    const count = inject("api").pipe(({api}, query: {id: string}) => {
        return api.users.count({})
    })

    return {
        getUser,
        count
    };
}

You can then use anywhere like this:

//anywhere.ts

import {UserService} from "<path>";
import {apiClient: prod} from "./api.prod"
import {apiClient: staging} from "./api.staging"

const myUserFromProd = UserService({api: prod});
const myUserFromStaging = UserService({api: staging});

Go deeper and split your services and repositories and abstractions:

//user.repository.prisma.ts
type UserRepository = {count: () => number};

export const UserRepositoryWithPrisma: AUserRepository = createConduit<{ prisma: PrismaClient; }>((inject) => {

    const count = inject(["prisma"]).in(({prisma}) => {
        return prisma.users.find({where: {id}})
    })

    return {
        count
    } satisfies AbstractUserRepository;
})

//user.repository.drizzle.ts

export const UserRepositoryWithDrizzle: AUserRepository = createConduit<{ drizzle: DrizzleClient; }>((inject) => {

    const count = inject(["drizzle"]).in(({drizzle}) => {
        return drizzle.users.length()
    })

    return {
        count
    } satisfies AbstractUserRepository;
})
//user.service.ts
import type {UserRepository} from "<path>"

export const UserService = createConduit<{ repository: UserRepository; config: any, repository: }>((inject) => {

    //Define related utils functions and export them
    const hashId = (username: string, joinTime: string) => hash([username, joinTime]);

    //Access data via repository
    const count = inject("repository").in(({repository}) => {
        return repository.count()
    })

    return {
        hashId,
        count
    };
}

And the cherry on top, You can use partial conduits ! Making it easy to group related code without having to provide more dependencies that your usecase needs and this provides amazing DX with Typescript.

const userServiceWithDrizzle = UserService({ repository: UserRepositorWithDrizzle({drizzle}) })

userServiceWithDrizzle.hashId("flip", "102938"); //✅
await userServiceWithDrizzle.count(); //✅

const minimalUserService = UserService({});

minimalUserService.hashId("flip", "102938"); //✅
await userServiceWithDrizzle.count(); //❌

Readme

Keywords

none

Package Sidebar

Install

npm i @merkl/conduit

Weekly Downloads

406

Version

0.0.4

License

ISC

Unpacked Size

17.6 kB

Total Files

11

Last publish

Collaborators

  • picodes_merkl
  • clmntngl