Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Apr 01 19:05
    cantoramann closed #234
  • Apr 01 19:05
    cantoramann commented #234
  • Apr 01 01:49
    coveralls commented #233
  • Mar 31 22:36
    jeffijoe commented #234
  • Mar 31 22:17
    cantoramann edited #234
  • Mar 31 22:17
    cantoramann edited #234
  • Mar 31 22:16
    cantoramann edited #234
  • Mar 31 22:15
    cantoramann opened #234
  • Mar 31 21:48
    dependabot[bot] labeled #233
  • Mar 31 21:48
    dependabot[bot] opened #233
  • Mar 31 21:48

    dependabot[bot] on npm_and_yarn

    chore(deps): bump y18n from 4.0… (compare)

  • Mar 05 15:19
    jeffijoe commented #232
  • Mar 05 15:16
    jdmorlan edited #232
  • Mar 05 15:12
    jdmorlan edited #232
  • Mar 05 15:11
    jdmorlan opened #232
  • Feb 28 13:41
    konfer-be edited #231
  • Feb 28 13:40
    konfer-be edited #231
  • Feb 28 13:40
    konfer-be opened #231
  • Feb 26 03:12
    disapointmntPanda closed #230
  • Feb 26 03:11
    jeffijoe commented #230
Roman
@moltar
Hi Jeff, how do you structure your setup?
Jeff Hansen
@jeffijoe
I just have a src with folder like services, stores, etc
Roman
@moltar
Are you saying you just register shared stuff in each service?
Jeff Hansen
@jeffijoe
Yeah
Roman
@moltar
Ah okay thank you
Jeff Hansen
@jeffijoe
Literally like
import { createRedis } from ‘@mycorp/my-commonscontainer.register({
  redis: asFunction(createRedis).singleton()
})
Roman
@moltar
I was thinking a bit like NestJS where the modules can export certain bits.
Yup. Got it! Thank you.
Jeff Hansen
@jeffijoe
You can do that too if you want, you can have a file that exports a map of registrations
Roman
@moltar
Right, true!
Literally just a regular export.
Jeff Hansen
@jeffijoe
Yup
Roman
@moltar
Didnt think of that :D
Jeff Hansen
@jeffijoe
export const registrations = {
  awesome: asFunction(makeAwesome)
}
Roman
@moltar
Sometimes the simplest is the best
I was actually thinking to export resolved services
export container.cradle
And/or cherry pick
Jeff Hansen
@jeffijoe
Not sure how well that works with lifetimes
The above works as if it was a single app
Roman
@moltar
Ah right
Mathieu Ghennassia
@mathieug
$> npx tsc
node_modules/awilix/lib/list-modules.d.ts:1:23 - error TS7016: Could not find a
 declaration file for module 'glob'. '/[...]/api/node_modules/awilix/node_modules/glob/glob.js' implicitly has an 'any' type.
  Try `npm install @types/glob` if it exists or add a new declaration (.d.ts) f
ile containing `declare module 'glob';`

1 import * as glob from 'glob';
Hi, what could be wrong?
Mathieu Ghennassia
@mathieug
@jeffijoe have you ever experienced this error?
Jeff Hansen
@jeffijoe
@mathieug you need skipLibCheck in your compilerOptions in tsconfig
Mathieu Ghennassia
@mathieug
Oh, didn’t know about this attribute
Thanks
Mathieu Ghennassia
@mathieug
Hello @jeffijoe, how do I debug loadModules()? It doesn't register any module. I have even checked with listModules().
container.loadModules(
  [
    [
      'modules/**/service.ts',
      {
        register: asClass,
        lifetime: Lifetime.SCOPED
      }
    ],
    [
      'modules/**/repository.ts',
      {
        register: asClass,
        lifetime: Lifetime.SINGLETON
      }
    ]
  ],
  {
    cwd: __dirname
  }
);
It works with ts-node but not with tsc..
Mathieu Ghennassia
@mathieug
I'm sorry, it should be .js. I'll keep .{ts,js}
Jeff Hansen
@jeffijoe
Yeah you need .{ts,js}.
Teddy Paul
@TdyP

Hi there! I've been using Awilix for almost 2 years now and really enjoying it.
I'm currently trying to namespace or group my deps. I stumbled upon this snippet on the conversation above:

function groupRegistrationsBy(predicate: (registrationName: string) => boolean): Resolver<any> {
    return {
        resolve: c =>
            _(c.registrations)
                .keys()
                .filter(predicate)
                .reduce(
                    (registrationsGroup, registrationName) => ({
                        ...registrationsGroup,
                        [registrationName]: c.resolve(registrationName)
                    }),
                    {}
                )
    };
}

container.register({
  repositories: groupRegistrationsBy(reg => _.endsWith(reg, 'Repository'),
  routers: groupRegistrationsBy(reg => _.endsWith(reg, 'Router')
})

This seems to be a reasonable approach for what I want to do but here you first register everything and then group things using these registrations. Is it possible to do it one shot: load modules and group it at the time ?

Jeff Hansen
@jeffijoe
@TdyP because you don't want the individual ones?
You can always invoke loadModules yourself and group + register the result.loadedModules
Essentially, write a function that does what these lines do: https://github.com/jeffijoe/awilix/blob/master/src/container.ts#L569-L584
Teddy Paul
@TdyP
Ok, I'll give it a try, thanks
At the moment, I'm having a problem with cyclic dependencies when using these grouped registrations. I have 1 group per layer (eg. app, domain, infra). So in a app module I need to access the domain, but if in the domain I need domain too, even though I won't use the same module in the end, it throws because of cyclic dependencies
I do understand why it crashes, it totally makes sense. I'm looking for a workaround
Jeff Hansen
@jeffijoe
Try not to overcomplicate it :)
Teddy Paul
@TdyP
Generally speaking, how would you do to somehow namespace registered modules (with no cyclic dependencies :) )?
I have some naming conventions, but it's just conventions, I'd like something more reliable. I thought about adding a suffix or prefix to module names, but it's not much more reliable than convention for me.
Teddy Paul
@TdyP

I managed to do it like that:

container.loadModules(['+(app|domain|infra)/**/!(*.spec).+(js|ts)'], {
        formatName: (name, descriptor) => {
            const splat = descriptor.path.split('/');
            const namespace = splat[splat.length - 2];
            return namespace + '.' + name;
        },
        resolverOptions: {lifetime: Lifetime.SINGLETON},
        cwd: __dirname
    });

It makes the job, using deps in modules is a bit verbose though:

import {ITestDomain} from 'domain/testDomain';

export interface ITestApp {
    myFunc(): string;
}

interface IDeps {
    ['domain.testDomain']: ITestDomain;
}

export default function({['domain.testDomain']: testDomain}: IDeps) {
    return {
        myFunc: () => testDomain.someFunc()
    };
}
Jeff Hansen
@jeffijoe
I wouldn't do it at all
Name your things exactly what they are
Daniel Park
@gimli01

has anyone worked with typescript and awilix? I'm using awilix for DI but i'm having an issue with some eslint errors saying that:

Property 'container' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'

import Status from "http-status-codes";
import { ErrorRequestHandler, NextFunction, Request, Response } from "express";
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  const { logger } = req.container.cradle;
};
Rodrigo Carranza
@DrecDroid

What do you think of this approach

import { createContainer, Lifetime } from 'awilix'

interface IDeps {
  [key: string]: any
}

function createAppContainer() {
  const container = createContainer<IDeps>()

  container.loadModules([`${__dirname}/**/providers/**/!(*.spec|*.d|*.test).{js,ts}`], {
    resolverOptions: {
      lifetime: Lifetime.SINGLETON,
    },
  })

  return container
}

export { createAppContainer, IDeps }

configProvider.ts

import { RESOLVER } from 'awilix'
import createConfig from '~/config'

type IConfig = ReturnType<typeof createConfig>
declare module '~/container' {
  interface IDeps {
    config: IConfig
  }
}

function configProvider(): IConfig {
  if (process.env.NODE_ENV !== 'production') {
    process.env.USE_DOTENV = 'true'
  }

  return createConfig()
}

configProvider[RESOLVER] = {
  name: 'config',
}

export default configProvider

redisProvider.ts

import Redis from 'ioredis'
import { RESOLVER } from 'awilix'
import { IDeps } from '~/container'

declare module '~/container' {
  interface IDeps {
    redis: ReturnType<typeof redisProvider>
  }
}

function redisProvider(deps: IDeps) {
  const { config } = deps

  const redisUrl = config.get('redis_url')

  const globalClient = new Redis(redisUrl)

  const clients: Record<string, Redis.Redis> = {}

  function getClient(name: string) {
    if (!name) {
      return globalClient
    }

    if (!clients[name]) {
      clients[name] = globalClient.duplicate()
    }

    return clients[name]
  }

  return {
    getClient,
  }
}

redisProvider[RESOLVER] = {
  name: 'redis',
}

export default redisProvider
lake-toya
@lake-toya

Is there any best practise for handling async services? I'm trying to get awilix working properly with puppeteer and mysql2/promise. Both of them have an async launch/connect function. I've read some stuff on Github about doing it this way:

const browser = await puppeteer.launch({...})

container.register({
  browser: asFunction(() => browser)
    .singleton()
    .disposer(browser => browser.close())
})

The problem I have with this solution is that the disposer is not called unless the service was resolved atleast once.

lake-toya
@lake-toya
I guess just doing it like this would work?
container.register({
  browser: asFunction(() => puppeteer.launch({...}))
    .singleton()
    .disposer(browserPromise => browserPromise.then(browser => browser.close()))
})

// and to resolve it
await container.resolve('browser')
Justin
@jayceekay
wondering if anyone has solved this already since i know TypeORM is a pretty popular ORM - trying to figure out how to nicely configure the container. typeorm wants registration like this:
container.register({ awilix.asFunction(() => connection.getCustomRepository(UserRepository) }); however i'd like to do this dynamically and without having to each repository's class name, ie. using loadModules. looking for the equivalent of something like this:
import { getCustomRepository } from 'typeorm';

container.loadModules(['src/repositories/**/*'], {
  register: (modules) =>
    modules.map((m) => asClass(getCustomRepository(m))
  ),
  resolverOptions: {
    injectionMode: InjectionMode.PROXY,
    lifetime: Lifetime.SINGLETON,
  },
});
i'm definitely missing something