Closed eithermonad closed 5 years ago
I do the same, but I only create a single root container per process. Why would you call it in all of your files?
@jeffijoe That's what I'm wondering. It sounded peculiar, which is why I asked.
If I'm using Express, and I have different routes/controllers in different files, is the only way to use Awilix though the awilix-expess
middleware? EDIT: Is that where scopePerRequest
comes in?
What about requiring the container in different integration test files?
If I'm using Express, and I have different routes/controllers in different files, is the only way to use Awilix though the awilix-expess middleware?
Not at all! At their core, the Express (and Koa, which I would recommend you use instead) Awilix middlewares could be expressed in as little code as this:
function inject(container, handler) {
return (req, res, next) => container.createScope().build(handler)(req, res, next)
}
However, the Express and Koa bindings have a neat "controller" declaration system which uses a router under the hood and also hides the whole container thing.
What about requiring the container in different integration test files?
If you are using Jest, each file usually gets their own environment (or at least your would-be singletons are run for as many cores you have on your machine, can't remember). Even so, I haven't seen any performance issues.
EDIT: Yeah, the library bindings' scopePerRequest
are basically
function scopePerRequest(container) {
return (req, res, next) => {
req.container = container.createScope()
next()
}
}
So when using inject
, it simply uses req.container
to build the handler.
@jeffijoe Thanks for the information.
All I need is to be able to pull my Service off the container inside the Controller and allow the Service to attain a Repository (again from the Container).
What's the best way of doing that?
I have my own auth middleware that puts a user
object onto each Controller for those that need it, so I don't need scopePerRequest
.
Here is an example. This is my architecture, so what is the best and cleanest method by which to wire up the container dependencies to all modules that need them?
const app = express();
const container = require('./configureContainer')();
// What do I do with this container inside my app file? How does it get passed to each controller, service, and repository?
// Different file.
class UserRepository {
constructor({ userModel }) {
this.userModel = userModel;
}
getUser(id) {
return userModel.findById(id);
}
}
// Different file.
class UserService {
constructor({ userRepository }) {
this.userRepository = userRepository;
}
getUser(id) {
return userRepository.getUser(id);
}
}
// Different file.
app.get('/user/:id', (req, res) => {
res.send(userService.getUser(req.params.id));
});
Again, thanks for your time.
I would strongly recommend using scopePerRequest
specifically for passing your user
to the controllers.
Your middleware would simply be:
function auth(req, res, next) {
req.container.register('user', asValue(req.user).scoped())
next()
}
Take a look at this boilerplate, it sounds like it covers everything you want. This bit and this bit might be of interest.
I'm usually lurking in the Koa Slack if you're thinking of making the switch :)
@jeffijoe Thank you for the resources. Why do you prefer Koa over Express?
I would strongly recommend using
scopePerRequest
specifically for passing youruser
to the controllers.
Not every endpoint requires an authenticated user. Some are publicly accessible.
Take a look at this boilerplate
Will that show how the model gets injected into the repository, how the repository gets injected into the service, and how the service gets injected into the controller, as per my updated comment?
Sorry to bother you with these questions. Thanks for your help and thanks for making Awilix.
Not every endpoint requires an authenticated user. Some are publicly accessible.
Still supported, simply register a default empty user context.
Will that show how the model gets injected into the repository, how the repository gets injected into the service, and how the service gets injected into the controller, as per my updated comment?
That's exactly what it does. π
@jeffijoe
Still supported, simply register a default empty user context.
So if I have
GET /products?skip=X&limit=Y
- Public
POST /products
- Protected
inside the same file, and I register with an empty user context, how is my GET Route going to get the empty context and my POST Route going to get the authenticated user
object?
EDIT:
In order to put user
on req.container
, then I would need my authentication middleware to be use
d before the call to create the scopePerRequest
, which means that every subsequent endpoint is going to want an authenticated user, even if those endpoints do not need to be protected.
Use scopePerRequest
first. In configureContainer
, do the following:
// Always provide a context. Initialize the user to `null`.
// This is available at all times.
container.register('context', asValue({ user: null }).scoped())
Make sure scopePerRequest
is added at the top-end of your middleware chain so it runs before all your auth and controller stuff.
In your auth middleware:
function auth(req, res, next) {
// In fact I would let requests with no auth parameters
// pass through and make the assertion in my controller.
req.container.resolve('context').user = req.user
next()
}
In your controller:
const { NotAuthenticated } = require('fejl') // shameless plug
class MyController {
constructor(context, productRepository) {
this.context = context
this.productRepository = productRepository
}
async createProduct(req, res) {
// Throw a NotAuthenticated if the user is not set.
NotAuthenticated.assert(this.context.user, 'You are not authenticated')
const product = await productRepository.create({
created_by: this.context.user.id,
...req.body
})
res.status(200).json(json)
}
}
I prefer Koa because its' middleware system makes more sense, it's more flexible, it's async, it's leaner and faster. See this. π
Most importantly, error handling is way better.
@jeffijoe
Thanks very much for your time. I appreciate it.
I'll do some more work tomorrow with your example and with the boilerplate you sent me. I'll let you know how it goes or if I have more questions (if that's okay?).
Thank you.
Sure! Slack is a better medium for instant communication though. π
@jeffijoe I'll sign up to the Slack link you sent.
Pertaining to my auth middleware function, I currently have this: (Not using DI - needs to be refactored.)
const jwt = require('jsonwebtoken');
const User = require('./../models/user');
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findOne({ _id: decoded._id });
if (!user) {
throw new Error();
}
req.user = user;
next();
} catch (e) {
res.status(401).send({ error: 'Please authenticate.' });
}
}
module.exports = auth;
And would use it like this:
app.get('/products', (req, res) => res.send());
app.post('/products', auth, (req, res) => res.send());
But, you are saying to do this:
const jwt = require('jsonwebtoken');
const User = require('./../models/user');
const auth = async (req, res, next) => {
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findOne({ _id: decoded._id });
req.user = req.container.resolve('context').user = user ? user : null;
next();
}
module.exports = auth;
And then, in a Controller, and suppose I have my own AuthenticationError
that extends
Error
, I'd assert that req.container.user
is not null
, like this:
class MyController {
// ctor ...
createProduct(req, res) {
if (!req.container.cradle.user) res.status(401).send();
// ...
}
}
Since that if
statement is going to be reused in many places, can that not be made into its own middleware and be reused in each endpoint that requires protection?
I still don't understand how context
and productRepository
get injected into MyController
, but perhaps I'll understand better if I take a closer look at the boilerplate you sent.
You can do the auth check however you want. DRY shouldn't be applied relentlessly, sometimes it makes more sense to have some composable one-liners so you can glance at a method and see how it flows as opposed to relying on some auth middleware defined elsewhere. Also, it doesn't have to be middleware! It could be as simple as
function requireAuth(context) {
if (!context.user) { throw new AuthError() }
return context.user
}
class Ctrl {
// ctor..
async doStuff(req, res) {
if (req.params.something) {
const user = requireAuth(this.context)
// Do something with user
} else {
// Dont require auth here maybe
}
}
}
The advantage is also to do selective auth. Like "if this parameter is specified, require that the user is authenticated".
Don't reference the container in your controller, inject the dependencies using awilix-express
. I recommend using loadControllers
.
In general, try to make HTTP (Express, Koa) an implementation detail of the outer "layer". If you some day want to expose your app over Websockets or TCP, you only need to write an "adapter" that translates websocket messages into your service calls. I've done this in practice where I have a service exposed over HTTP as well as Websocket, where the service was using .scoped()
lifetime and would create an in-memory cache that would be scoped to the request, but when using websockets it would be scoped to the websocket connection.
@jeffijoe Thank you.
I understand. The Controller layer should be very thin, sitting on top, and should only handle I/O of data. It also should never pass a req
or res
object directly to the service because then you would be coupling the service with either the HTTP Framework or the WebSocket implementation, etc.
I'll look into the loadControllers
function in the docs for awilix-express
- it sounds like what I'm looking for in terms of injecting services and repositories.
Thanks.
@jeffijoe
NOTE: See additional edits at the bottom.
I'm still a little stuck over what the best way to accomplish injection is. If you have time, I'd really appreciate it if you could help with this last question. I've attempted to show an extremely simple use case just to help me get my head around how Awilix
and awilix-express
works. The comments should show the questions I have. It would be helpful if you could show how this would be refactored to use Awilix
.
Thanks again for your time.
/* ////////// Products ////////// */
// File: ProductService.js
class ProductService {
constructor({ productRepository }) { /* ... */ }
getProducts(limit, skip) {
return productRepository.getAll(limit, skip);
}
postNewProduct(productInfo) {
return productRepository.save(productInfo);
}
}
// File: ProductRepository.js
class ProductRepository {
constructor({ productModel }) { /* ... */ }
getAll(limit, skip) {
return productModel.find().paginate(limit, skip);
}
save(productInfo) {
return productModel.save(productInfo);
}
}
/* ////////// Users ////////// */
// File: UserService.js
class UserService {
constructor({ userRepository }) { /* ... */ }
signUpNewUser(userInfo) {
return userRepository.save(userInfo);
}
}
// File: UserRepository.js
class UserRepository {
constructor({ userModel }) { /* ... */ }
save(userInfo) {
return userModel.save(userInfo);
}
}
/* ////////// Routes ////////// */
// File: products.js
const router = new express.Router();
router.get('/products', (req, res) => {
// How do I get productService into this endpoint?
const products = productService.getAll(10, 0);
res.send(products);
});
router.post('/products', auth, (req, res) => {
// How do I handle authentication in this endpoint?
// How do I get productService in this endpoint?
const product = productService.postNewProduct(req.product);
res.send(product);
});
module.exports = router;
// File: user.js
const router = new express.Router();
router.post('/users', (req, res) => {
// How do get userService in this endpoint?
const user = UserService.signUpNewUser(req.user);
res.send(user);
});
router.get('/users/me', auth, (req, res) => {
// req.user is currently coming from my auth middleware. This should use the container instead, right?
res.send(req.user);
});
module.exports = router;
/* ////////// Auth Middleware ////////// */
// auth.js
const auth = async (req, res, next) => {
try {
// The Authorization Bearer Token sent in the header of the request needs to be decoded.
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = await admin.auth().verifyIdToken(token);
// Finding that user in the database by their Firebase UID.
const user = await User.findOne({ _id: decoded.uid });
if (!user) {
throw new Error();
}
// Making the user accessible to the endpoint.
req.user = user;
// Proceed
next();
} catch (e) {
// HTTP 401 Unauthorized Response Status
res.status(401).send({ error: 'Please authenticate.' });
}
}
module.exports = auth;
EDIT:
I've been reading the docs again and testing with cURL
, and it seems that I can just do this:
const express = require('express');
const awilix = require('awilix');
const { scopePerRequest } = require('awilix-express');
const app = express();
// This would be in a loader for awilix.
const UserService = require('./services/UserService');
const UserRepository = require('./repositories/UserRepository');
const UserModel = require('./models/user');
// Would be inside configureContainer factory function.
const container = awilix.createContainer();
container.register({
userService: awilix.asClass(UserService),
userRepository: awilix.asClass(UserRepository),
userModel: awilix.asValue(UserModel)
});
// App middleware.
app.use(scopePerRequest(container))
// Routes file.
app.post('/users', (req, res) => {
const user = { username: 'Jamie' };
req.container.cradle.userService.signUpNewUser(user);
res.send('Complete');
});
// server.js
app.listen(3000, () => console.log('Server is up'));
I didn't know that Awilix would automatically be able to inject dependencies into other dependencies via destructuring, so it makes sense now.
With that said, you mentioned not to use req.container.cradle
inside of an endpoint, so I'm still unsure what the best way is to actually resolve dependencies inside of my enpoints.
Keep using scopePerRequest
, then use inject
for the simplest way.
Example:
const { inject } = require('awilix-express')
// Routes file.
app.post('/users', inject(({ userService }) => (req, res) => {
const user = { username: 'Jamie' };
userService.signUpNewUser(user);
res.send('Complete');
}));
inject
will use the req.container
that scopePerRequest
configures to inject dependencies into a request handler.
For auth, you can do:
const auth = async (req, res, next) => {
try {
// The Authorization Bearer Token sent in the header of the request needs to be decoded.
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = await admin.auth().verifyIdToken(token);
// Finding that user in the database by their Firebase UID.
const user = await User.findOne({ _id: decoded.uid });
if (!user) {
throw new Error();
}
// Making the user accessible to the endpoint.
req.user = user;
// Register the user in the container so we can inject it into services
req.container.register('user', asValue(user))
// Proceed
next();
} catch (e) {
// HTTP 401 Unauthorized Response Status
res.status(401).send({ error: 'Please authenticate.' });
}
}
Now you can depend on user
in your service, given that you've registered it with scoped lifetime (hence scopePerRequest
π )
@jeffijoe Thank you very much. I understand now.
Is it possible to use inject
as global middleware in any way so that I don't have to use it in every single endpoint?
With the auth
example, do I still use auth
inside each endpoint, i.e, app.get('/protected', auth, inject ...);
?
Is it possible to use inject as global middleware in any way so that I don't have to use it in every single endpoint?
This is what the controller
is for in awilix-express
and awilix-koa
. π
With the auth example, do I still use auth inside each endpoint, i.e, app.get('/protected', auth, inject ...);?
Yes.
@jeffijoe Thank you.
Also, will Awilix overwrite files of the same name? That is, if I attempt to autoload:
src/api/routes/user.js,
src/models/user.js,
...
will the route be overwritten by the model?
Yes it will overwrite. You can customize how the registration name is generated from the file path though. See formatName
in the docs. Example:
// to customize how modules are named in the container (and for injection)
container.loadModules(['repository/account.js', 'service/email.js'], {
// This formats the module name so `repository/account.js` becomes `accountRepository`
formatName: (name, descriptor) => {
const splat = descriptor.path.split('/')
const namespace = splat[splat.length - 2] // `repository` or `service`
const upperNamespace =
namespace.charAt(0).toUpperCase() + namespace.substring(1)
return name + upperNamespace
}
})
@jeffijoe I see, thanks. Or, perhaps I could just name them as user.service.js
, and user.route.js
, and user.repository.js
, etc..
I think I prefer your method, however.
Sorry for asking all of these questions - this is my first time using Dependency Injection. I've used Express many, many times, but never DI nor Awilix with Express, so, as you might imagine, I'm quite confused. I think I'm getting there, however. Thanks.
No problem! Did you read my Medium series on DI with Awilix? It should explain a lot of this.
Use
scopePerRequest
first. InconfigureContainer
, do the following:// Always provide a context. Initialize the user to `null`. // This is available at all times. container.register('context', asValue({ user: null }).scoped())
Make sure
scopePerRequest
is added at the top-end of your middleware chain so it runs before all your auth and controller stuff.In your auth middleware:
function auth(req, res, next) { // In fact I would let requests with no auth parameters // pass through and make the assertion in my controller. req.container.resolve('context').user = req.user next() }
In your controller:
const { NotAuthenticated } = require('fejl') // shameless plug class MyController { constructor(context, productRepository) { this.context = context this.productRepository = productRepository } async createProduct(req, res) { // Throw a NotAuthenticated if the user is not set. NotAuthenticated.assert(this.context.user, 'You are not authenticated') const product = await productRepository.create({ created_by: this.context.user.id, ...req.body }) res.status(200).json(json) } }
This comment you sent a few hours ago ^.
My main point of misunderstanding right now is how productRespository
will get into MyController
- i.e, how is that set up on the Express side in my app.js
/server.js
file.
That happens when resolving the controller which you do with inject
No problem! Did you read my Medium series on DI with Awilix? It should explain a lot of this.
I did. I think I should read the last article a second time, however.
They were very nice and well-written articles, by the way. Not many people can inject (pardon the pun) humor into the text that way and still manage to write something informative. They are some of the best I've seen on Medium.
Haha, happy to hear it!
That happens when resolving the controller which you do with inject
Right, so how do I use inject
in that way but globally, such that I don't have to manually write inject
into each endpoint?
By letting awilix-express handle the routing. See the βnew in v1β section in the docs.
By letting awilix-express handle the routing. See the βnew in v1β section in the docs.
How do I selectively choose which routes have auth
middleware when doing it that way?
Using before
- see the Awesome Usage section.
But again, I would always run the auth middleware and leave it up to the service to determine whether auth is required. It gives you more flexibility, like auth is only required if the product is marked private.
Using
before
- see the Awesome Usage section.
I had thought that - I must have read the entire docs five times now. What about file uploading with Multer? Can I use before
in different places or will each before
affect all routes beneath it, similar to fall through?
But again, I would always run the auth middleware and leave it up to the service to determine whether auth is required. It gives you more flexibility, like auth is only required if the product is marked private.
In which case, I might have two middleware functions - an attachUser
to register a user object into the container, whether it's null or not, and a verifyAuth
to check whether user
is null, and if so, return 401.
Is there not any way to inject deps into endpoints without having to make controller classes/factories and without having to do it manually for every endpoint?
Thanks.
You can use before per route and per controller.
You can just pull dependencies out of req.container.cradle but I donβt see why you wouldnt just use the controllers feature or even just plain inject?
What if you want to verify auth based on some attributes from a product you have to fetch? Adding a verifyAuth middleware would not give you that flexibility, and you are coupling your authz to HTTP.
@jeffijoe
I'll just use inject
manually for now. I'm on a timeline with regards to completing this project, so I'll spend more time learning awilix-express
and refactor later to use a more maintainable pattern.
I agree with you about Auth and HTTP. To decouple them, I have my own AuthenticationError
that extends Error
, and my auth middleware throws that error. That doesn't, however, abstract away the process of verifying the JWT Bearer Token from the Authorization Header. I do use an Adapter to simplify the process, however.
I use (express-async-errors)[https://www.npmjs.com/package/express-async-errors] to allow me to catch errors inside of a bottom app.use
middleware function. That middleware function looks at the type of error, and if it's one of the errors I created, it pulls that error message and status code off that error object and sends it to the client, otherwise, the client gets a 500.
Thanks very much for your help. I apologize for taking up so much of your time.
@jeffijoe Just to check, should I load all my third-party dependencies into the Container as well, such as the Node AWS SDK, the Firebase Admin SDK, the UUID Library, etc.?
Only if you think you're getting value from that. I inject SDK's (because they require configuration) but not simple utilities like UUID.
@jeffijoe Thank you.
The only thing would be, with UUID, for example, that I could mock it to always give me the same random ID so that tests can be easier.
I suppose, however, that a Jest Manual Mock would do just fine.
In terms of injecting the Firebase Admin SDK or the Node AWS SDK, again, I can mock then manually with Jest, but I'm not sure if that goes against DI principals. Registering them in the container, however, might present problems with ensuring they are fully configured prior to said registration.
From: Jeff Hansen notifications@github.com Sent: Wednesday, June 19, 2019 1:30:18 AM To: jeffijoe/awilix Cc: Jamie Corkhill; Author Subject: Re: [jeffijoe/awilix] How should I configure the container? (#137)
Only if you think you're getting value from that. I inject SDK's (because they require configuration) but not simple utilities like UUID.
β You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/jeffijoe/awilix/issues/137?email_source=notifications&email_token=AG2ZVR7GFWZ36XCRLJWSPZDP3HG7VA5CNFSM4HY37TCKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYA2W2A#issuecomment-503425896, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AG2ZVR3SUNHQKHDFXCJPEULP3HG7VANCNFSM4HY37TCA.
Well if you already have a DI setup, if you do need to mock UUID then go ahead and inject UUID. You shouldn't have to use Jest module mocks for anything.
Injecting the SDKs, again, if you intend to mock them, sure. If not, do whatever you feel is more pragmatic. π
@jeffijoe
Alright - I suppose, then, in a Unit Test, I could register a new Container and overwrite with mocks, whereas, in an Integration Test, I could call the containerFactory
again from earlier to get everything. You also said it was okay to register a new container with hundreds of dependencies in each suite without a performance loss, I believe.
Additionally, if I wanted to use the same Mock across Unit Test Suites, then I could write the mock as a fixture, export it, and register a new container in one of the before
hooks in Jest.
Indeed, I define Adapters for my SDKs, so perhaps I could just do something like this:
// Amazon Web Services and Amazon Web Services Mock
const aws = require('./../../config/aws/aws');
const awsMock = require('mock-aws-s3');
const FileCRUDAdapter = require('./FileCRUDAdapter');
const { cloudStorageConfig } = require('./../../config/config');
if (process.env.MODE === 'test') awsMock.config.basePath = '/tmp/buckets';
const s3 = process.env.MODE === 'test' ? (
awsMock.S3({ params: { Bucket: cloudStorageConfig.buckets.testing.name }})
) : (
new aws.S3()
);
// FileCRUDAdapterFactory (function)
module.exports = (bucket = undefined) => new FileCRUDAdapter(s3, bucket);
Also, I have a factory function that creates a MongoDB Connection. I don't believe that needs to use DI, right, because I only require it in one file and call the factory before the Express Routes are set up, so I think that's fine to leave the way it is.
And thanks, by the way.
TBH your unit tests should not need to use a container, you should just construct your SUT (Subject Under Test) normally and provide whatever mocks you need.
I usually create my DB connections in configureContainer
(see my Koa boilerplate), register them with Awilix and let Awilix dispose them on shutdown (container.dispose()
).
@jeffijoe
I see. Thanks. So it would be normal for the file that houses the configureContainer
factory function to have many require statements when it comes to third-party NPM Modules if and only if those modules are being mocked?
Don't have to be mocked, if it just makes sense to inject a "prepared" module (like an SDK) then you can. If you'd rather just import the module directly because it is part of the implementation and is not something you'd want to mock, just import it.
So it would be normal for the file that houses the configureContainer factory function to have many require statements
Absolutely! In fact that's the point! It's your composition root.
@jeffijoe Great! Thanks.
So would this setup be considered normal? I don't want any calls to the Firebase Servers to initialize the application to occur, however, in test mode. So perhaps I make the firebase.js
file below return factory function, and inject admin
based on the environment.
config/firebase/firebase.js:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert(process.env.GOOGLE_APPLICATION_CREDENTIALS),
databaseURL: "[URL]"
});
module.exports = admin;
Composition Root:
const awilix = require('awilix');
// Third-party pre-configured SDKs:
const admin = require('./../config/firebase/firebase');
// Default Factory Function
module.exports = () => {
// Create the DI Container.
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.CLASSIC
});
// Load modules.
container.loadModules([
[
'../custom-exceptions/index.js',
{
register: awilix.asValue,
lifetime: awilix.Lifetime.TRANSIENT,
}
]
], {
cwd: __dirname,
})
.register({
admin: awilix.asValue(admin) // RIGHT HERE
});
return container;
};
An Adapter getting the SDK injected:
module.exports = class AdminAdapter {
constructor({ admin, AuthenticationError }) {
// Dependency Injection
this.admin = admin;
this.AuthenticationError = AuthenticationError; // A custom exception.
}
async verifyAuthToken(token) {
try {
const decoded = await this.admin.auth().verifyIdToken(token);
return decoded;
} catch (err) {
throw new this.AuthenticationError();
}
}
}
Is it alright to be asking questions here like this, or am I taking up too much of your time?
Thanks.
Yeah that looks reasonable, although not sure why you are injecting the error classes though, and also asValue does not use lifetime. π
Its fine, I donβt mind helping out, but it would be easier on the Koa Slack (and you really should consider Koa π)
@jeffijoe I will consider Koa. This project is for a startup, I'm the only developer, and I'm on a 1.5-month timeline for getting an MVP in front Investors, so, for now, I'll stick with Express since I know it best. Eventually, I'll be refactoring my codebase to use TypeScript instead of JavaScript, and to use PostgreSQL instead of MongoDB, so I'll ponder a possible Koa migration then.
For Slack, do you mean the Private Messaging feature and that we can use that to talk?
Yeah, or you can ask your general design-related questions in the general chat and others can chime in too.
@jeffijoe Alright. Thanks. I'll see you there.
In your Medium article, you create a factory function to do the work of configuring/registering the container:
I'm using
container.loadModules
, so I was wondering if it's alright to do something like this:What I'm worried about, however, is that calling this function in all of my files is going to make Awilix go out and try to autoload all of my dependencies again.
If I have ten other files, and I do this at the top of each file:
Will Awilix attempt to register the container ten other times and be extremely latent (if I have hundreds of deps), or will using
require
make the factory function a singleton (I worry about module caching issues in different scopes), or does Awilix perform its own internal caching of registered dependencies behind the scenes?Thank you.