How to use inversifyjs container as service locator - node.js

I'm writing socket server for my IOT project (stm32f100 + WIFI esp)
I have implemented communication protocol such as GET query:
method=test&data[]=1&data[]=2...etc
I want to implement method resolver which will fetch service (controller) by method name from inversifyjs container with it's dependencies, but I can't find how to inject container into method resolver

You can have multiple controllers:
container.bind<Controller>("Controller")
.to(TestController)
.whenTargetNamed("test");
container.bind<Controller>("Controller")
.to(UserController)
.whenTargetNamed("user");
container.bind<Controller>("Controller")
.to(OrderController)
.whenTargetNamed("order");
...
Then you can implement something like the following:
function getService(service: string) {
container.getNamed<Controller>("Controller", service);
}
You can then resolve based on the method argument:
const orderController = getService("order");
const userController = getService("user");

Related

How to create a factory for a typeorm custom repository in nestjs?

I have a repository:
export class MyRepository
extends
Repository<MyEntity>
{
constructor(
protected readonly _clientId: string
) {
super()
}
// ... methods
}
I need to pass the client id through which isnt known until request time. As such, the only way I know how to do it would be to create a factory which can create the repo at run time (it's in GRPC metadata).
#Injectable()
export class MyRepositoryFactory {
create(clientId: string) {
return new MyRepository(
clientId,
);
}
}
I register this as a provider and then in my controller I call:
const { clientId } = context;
const repository = this.myRepositoryFactory.create(clientId);
However I get the error
"Cannot read property 'findOne' of undefined"
when trying to do a basic typeorm call. I can see this is because instead the repository should be registered in the module imports like:
imports: [
TypeOrmModule.forFeature([ MyRepository, MyEntity ])
],
However this only works when injecting the repository directly, not in a factory. I have no idea how to either overcome this problem, or use a different way of creating the repository at run time with GRPC meta data passed through. Any help would be appreciated, thanks.
you cant call new and have the repository connected to TypeORM.
for that you need to make call to getCustomRepository(MyCustomRepository (or connectio.getCustomRepository) so it would be connected to the connectiong pool and everything TypeORM hides under the hood.
IMO creating a custom repository per request is not such a great idea, maybe you can create a Provider that has scope REQUEST (so the provider would be created per-request) allowing NestJS to inject the repository the 'standard' way, modify it's client attribute and then use it's methods that uses the internal repository methods.
something like
#Injectable({ scope: Scope.REQUEST })
export class RequestRepositoryHandler {
client_id: string
constructor(#InjectRepository(MyEntity) private repo: Repository<MyEntity>){}
// other methods
}
// usage
#Injectable()
export class SomeService {
constructor(private providerRepo: RequestRepositoryHandler){}
async method(client_id: string){
this.providerRepo.client_id = client_id; // the provider is per-request
// now do some work
}
}

NestJS: How to register transient and per web request providers

I am working on a multi-tenant application using NestJS and offering my API through their GraphQL module. I would like to know how I could tell NestJS to instantiate my providers per web request. According to their documentation providers are singleton by default but I could not find a way to register transient or per request providers.
Let me explain a specific use case for this. In my multi-tenant implementation, I have a database per customer and every time I receive a request in the backend I need to find out for which customer it is so I need to instantiate services with a connection to the right database.
Is this even possible using NestJS?
With the release of nest.js 6.0, injection scopes were added. With this, you can choose one of the following three scopes for your providers:
SINGLETON: Default behavior. One instance of your provider is used for the whole application
TRANSIENT: A dedicated instance of your provider is created for every provider that injects it.
REQUEST: For each request, a new provider is created. Caution: This behavior will bubble up in your dependency chain. Example: If UsersController (Singleton) injects UsersService (Singleton) that injects OtherService (Request), then both UsersController and UsersService will automatically become request-scoped.
Usage
Either add it to the #Injectable() decorator:
#Injectable({ scope: Scope.REQUEST })
export class UsersService {}
Or set it for custom providers in your module definition:
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
Outdated Answer
As you can see in this issue, nestjs does not yet offer a built-in solution for request-scoped providers. But it might do so in the near future:
Once async-hooks feature (it is still experimental in node 10) is
stable, we'll think about providing a built-in solution for
request-scoped instances.
I was struggling with similar issue, and one way to achieve this is to use node-request-context module as a global request register, that will give you the request context. So you will not have separate service instances, but you can ask this static register to give you request specific instance/connection.
https://github.com/guyguyon/node-request-context
Create simple context helper:
import { createNamespace, getNamespace } from 'node-request-context';
import * as uuid from 'uuid';
export class RequestContext {
public static readonly NAMESPACE = 'some-namespace';
public readonly id = uuid.v4();
constructor(public readonly conn: Connection) { }
static create(conn: Connection, next: Function) {
const context = new RequestContext(conn);
const namespace = getNamespace(RequestContext.NAMESPACE) || createNamespace(RequestContext.NAMESPACE);
namespace.run(() => {
namespace.set(RequestContext.name, context);
next();
});
}
static currentRequestContext(): RequestContext {
const namespace = getNamespace(RequestContext.NAMESPACE);
return namespace ? namespace.get(RequestContext.name) : null;
}
static getConnection(): Connection {
const context = RequestContext.currentRequestContext();
return context ? context.conn : null;
}
}
The conn instance parameter is your connection, feel free to put there other request specific dependencies. Also the id there is just for debugging, no real need to use uuid module as I did.
Create middleware wrapper (this allows you to use DI here):
#Injectable()
export class ContextMiddleware implements NestMiddleware {
constructor(private readonly connectionManager: ...) { }
resolve(...args: any[]): MiddlewareFunction {
return (req, res, next) => {
// create the request specific connection here, probably based on some auth header...
RequestContext.create(this.connectionManager.createConnection(), next);
};
}
}
Then register new middleware in your nest application:
const app = await NestFactory.create(AppModule, {});
app.use(app.get(RequestLoggerMiddleware).resolve());
And finally the profit part - get the request specific connection anywhere in your application:
const conn = RequestContext.getConnection();
NestJS has a build-in request-scope dependency injection mechanism
https://docs.nestjs.com/fundamentals/injection-scopes
but it has serious drawbacks according to the documentation:
Scope bubbles up the injection chain. A controller that depends on a request-scoped provider will, itself, be request-scoped.
Using request-scoped providers will have an impact on application performance. While Nest tries to cache as much metadata as possible, it will still have to create an instance of your class on each request. Hence, it will slow down your average response time and overall benchmarking result. Unless a provider must be request-scoped, it is strongly recommended that you use the default singleton scope.
Recently I have created request-scope implementation for NestJS free from bubbling up the injection chain and performance impact.
https://github.com/kugacz/nj-request-scope
To use it first you have to add import of RequestScopeModule in the module class decorator:
import { RequestScopeModule } from 'nj-request-scope';
#Module({
imports: [RequestScopeModule],
})
Next, there are two ways of request-scope injection:
Inject express request object into class constructor with NJRS_REQUEST token:
import { NJRS_REQUEST } from 'nj-request-scope';
[...]
constructor(#Inject(NJRS_REQUEST) private readonly request: Request) {}
Change class inject scope to request-scope with #RequestScope() decorator:
import { RequestScope } from 'nj-request-scope';
#Injectable()
#RequestScope()
export class RequestScopeService {
You can find example implementations in this repository: https://github.com/kugacz/nj-request-scope-example

Parameter discovery is not supported for connections using GenericDatabase.

I have been trying create my own DbProvider and configure it with EnterpriseLibrary but I'm running through below issue
Parameter discovery is not supported for connections using GenericDatabase. You must specify the parameters explicitly, or configure the connection to use a type deriving from Database that supports parameter discovery.
I have Created Connection class which implements DbConnection and other classes like classes which implmenets DbCommand etc
I have overriden the DbConnection class and the methods which I dont know implementation I have thrown Not implemented exception like
public override string Database
{
get
{
throw new NotImpelmentedException();
}
}
And I have created enterpriselibrary connection as below
public Database GetConnection()
{
CustomDbProviderFactory obj = new CustomDbProviderFactory();
GenericDatabase db = new GenericDatabase("server=****;Request Timeout=3000000;", obj);
return db;
}
but when I execute DataReader with conection string and command I 'm getting issue I have mentioned
I have resolved this issue,
While using generic database object , I was using like this
db.ExecuteReader("Select * from employees");
instead it should be
db.ExecuteReader(CommandType.Text,"Select * from employees");

extend the CRUD method in LoopBack

I would like to know how to extend CRUD methods created by LoopBack.
I have a model with an attribute public_key. I would like to build two custom behaviors for the POST api endpoint for this model.
generate a public key and set the value
generate a private key and send it back as a result (using SSL)
How can I extend the default method to implement these behaviors?
I was able to override the default method by creating a javascript file under server/boot.
module.exports = function(app) {
var MyModel = app.models.MyModel;
var create = MyModel.create;
// Overrides POST '/api/MyModel' endpoint
MyModel.create = function(data, done) {
// Do custom things
create.call(MyModel, data, done);
};
};
However I was not able to modify the return value.
You can define custom remote methods that are exposed as REST endpoints

Why can't I change the scope of my object with ServiceStacks IoC?

Given the following code from my Configure method:
OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(ConfigUtils.GetConnectionString("Oracle:FEConnection"), OracleOrmLiteDialectProvider.Instance);
container.Register<IDbConnectionFactory>(dbFactory)).ReusedWithin(ReuseScope.Request); // <== this does NOT work
// But these work
container.Register<IPreprocessorRepository>(c => new CachedPreprocessorRepository(dbFactory, c.Resolve<ICacheClient>())).ReusedWithin(ReuseScope.Default);
container.Register<IPreprocessor>(c => new DirectApiPreprocessor(c.Resolve<IPreprocessorRepository>(), c.Resolve<IValidator<LeadInformation>>())).ReusedWithin(ReuseScope.Default);
How can I make sure that the dbFactory instanciated is used in other registrations will per request?
Thank you,
Stephen
You can't change the scope of this:
container.Register<IDbConnectionFactory>(dbFactory)
.ReusedWithin(ReuseScope.Request);
Because you're only passing in an instance of an object, and not a factory function that the IOC would need to be able to instantiate instances of the object itself. All the IOC can do in this case is return the instance, making it a singleton.
To be able to change the scope you would need to register a delegate that can create an instance, i.e:
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(...))
.ReusedWithin(ReuseScope.Request);
But you never want to do this with any connection or client factories like IDbConnectionFactory or IRedisClientsManager since they're designed to be used as singletons.
i.e. They're thread-safe singleton factories used to create single client/connection instances:
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
//...
}
using (var redis = container.Resolve<IRedisClientsManager>().GetClient())
{
//...
}

Resources