Resource handler function - how to inject class
Hi, how can I inject my nestjs service class (injectable) in my custom handler function in resource?
`customFunction: {
handler: async (
request: ActionRequest,
response: ActionResponse,
context: ActionContext,
) => {
// here inject my service with all dependencies
return { };
},
},`
Is it possible?
Can anyone help with this? I am also looking for a way to inject or instantiate a Service which need dependency injection by NestJs. Not sure how to do it manually in a handler or DefaultAuthProvider.authenticate method.
@devwork-g We usually do not set up AdminJS + NestJS via @adminjs/nestjs but use @adminjs/express directly in main.ts:
await setupAdmin(app);
Example:
const setupAdmin = async (app: INestApplication): Promise<void> => {
const configService = app.get(ConfigService);
const expressApp: AbstractHttpAdapter = app.get(HttpAdapterHost).httpAdapter;
const serviceAccessor = getServiceAccessor(app);
const adminJS = new AdminJS(getAdminJSOptions(serviceAccessor));
if ([Environment.PRODUCTION, Environment.STAGING].includes(configService.get('application.environment'))) {
await adminJS.initialize();
} else {
adminJS.watch();
}
const router = await getRouter(adminJS, serviceAccessor);
expressApp.use(adminJS.options.rootPath, router);
};
getRouter and getAdminJSOptions are helpers for building an Express router and AdminJS options config. As you can see serviceAccessor is passed to both of these helpers. Service accessor is used for accessing any provider registered in your application, it's not module-scoped and you simply pass the provider class to it, i.e.:
const configService = serviceAccessor.getService(ConfigService);
Create a Service Accessor:
import { Abstract, INestApplication, Type } from '@nestjs/common';
export interface ServiceAccessor {
getService: <T>(service: Type<T> | Abstract<T> | string | symbol) => T;
}
export const getServiceAccessor = (app: INestApplication): ServiceAccessor => ({
getService: <T>(service: Type<T> | Abstract<T> | string | symbol): T => app.get<T>(service),
});
You can then pass it further:
const getAdminJSOptions = (serviceAccessor: ServiceAccessor): AdminJSOptions => {
return {
resources: [
createUserResource(serviceAccessor),
],
};
};
export const createUserResource = (serviceAccessor: ServiceAccessor): CreateResourceResult<typeof User> => ({
resource: User,
options: {
actions: {
edit: {
after: [someAfterHook(serviceAccessor)],
}
}
}
})
Alternatively, you could create a middleware which extends the Request object with serviceAccessor, the request object is available in all before/after hooks and handlers. You should be able to instantiate AdminJS nest-way then.
@dziraf Wow!! Thanks a lot for such a quick and detailed reply. This solves my issue.
@dziraf As I tried to implement a middleware to attatch custom field to Req I have noticed that whether I use functional middleware or Module scopped middleware, they are not getting executed when Adminjs specific URL are called, like if call http://localhost:3000/admin/login then middleware wont be executed. it works otherwise if I call any of my controller.
A sample code I used to register a middleware in main.ts
console.log('Before Middleware executed');
app.use((req, res, next) => {
console.log('Middleware being executed'); // <- this doesn't shows up for Adminjs specific routes
const serviceAccessor = getServiceAccessor(app);
req['serviceAccessor'] = serviceAccessor as ServiceAccessor;
next();
})
My setup is AdminJS with NestJs only. Is there anything I am missing here?
@dziraf Any guidance if you can please provide here.