Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guidance for async dependencies #1

Closed
moltar opened this issue Oct 3, 2021 · 6 comments
Closed

Guidance for async dependencies #1

moltar opened this issue Oct 3, 2021 · 6 comments

Comments

@moltar
Copy link

moltar commented Oct 3, 2021

Looks like a nice and clean ioc implementation.

Wondering about binding async dependencies?

For example a pg database connection.

Should it be resolved prior to binding then? Would be nice to have that documented and maybe added to the example repo.

Thank you.

@tlaanemaa
Copy link
Owner

tlaanemaa commented Oct 3, 2021

Hey

Thank you!

Since tinioc gives you whatever comes back from your factory then you can just return a promise.
For a pg connection, I'd imagine you'd want to use a singleton so you could do something like this:

// dbClient.ts

import { Client } from "pg";

/**
 * Creates and connects a pg client
 */
const getConnectedClient = async () => {
  const client = new Client();
  await client.connect();
  return client;
};

/*
  I'd recommend to move the `Promise<Client>` type to the bindings file
  for decoupling benefits
*/
const client = getConnectedClient();
export const dbClient = (): Promise<Client> => client;

This essentially gives you an async component, since the component is a promise.
Now when injecting it with inject, you'll get a promise so you can await it to get the connection.
Tinioc doesn't really do anything with what is returned from the factory so you're free to pass it however you like :)

Good question btw, I'll make note to add a section about this to the readme.
Let me know if you need any more help!

@tlaanemaa tlaanemaa reopened this Oct 3, 2021
@tlaanemaa
Copy link
Owner

Sry, accidentally closed it prematurely :/

@tlaanemaa
Copy link
Owner

I've updates the readme and example too 👍

@moltar
Copy link
Author

moltar commented Oct 4, 2021

That's what I was afraid of 😁 That every async dependency will need to be resolved manually.

This is the same design that awilix follows too.

An interesting approach is taken by the typesafe-di package, but the interface is very complex.

The reason I am concerned about this, is because the design requires one of the two solutions, both of which I find to be not so elegant:

Solution 1: Resolve manually

function useCase() {
  const client = await getConnectedClient();
}

Solution 2: Use bootstrap function for container building

async function bootstrap() {
  const client = new Client();
  const db = await client.connect();

  container.bind<bindings.DB>(bindings.DB, db);

  return container
}

await bootstrap().then((container) => {
  // app code goes here
})

@tlaanemaa
Copy link
Owner

tlaanemaa commented Oct 6, 2021

I think the async injection (resolve manually) solution is actually quite elegant as it conveys very clearly what's happening. It is an async dependency so you're injecting it async too. You just have to make sure not to inject at component creation, else your component will also become async.

Another approach would be to hide the asynchronicity in the actual method call. For example, you can hide the DB connection promise inside the .query method. This way, the connection creation is synchronous which means that your component is also synchronous. The query to the DB would anyway be async so moving the connection wait there changes nothing.
I believe this is the approach pg uses when you're using the Pool, the Pool creation is sync.

I hope this helps

@tlaanemaa
Copy link
Owner

I'll close this since it seems inactive.
Please feel free to reopen if needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants