#Using services outside module

18 messages · Page 1 of 1 (latest)

narrow mural
#

Hello. I'm new to Nest.js and I struggle with a thing I think should be easy but I don't know how to do it cluefulbutbad

I have a file containing a module, which is straight imported by app.module.ts:

import { Module } from '@nestjs/common';
import { AdminModule } from '@adminjs/nestjs';
import { UserEntity } from 'src/database/entities/user/user.entity';

import * as AdminJSTypeorm from '@adminjs/typeorm';
import AdminJS from 'adminjs';
import theme from '@adminjs/design-system';
import { ChannelEntity } from 'src/database/entities/channel/channel.entity';
import { UserResource } from './resources/user.resource';
import { AdminPanelLocale } from './admin-panel.locale';
import { ChannelResource } from './resources/channel.resource';
import { componentLoader } from './components/components';
import { AuthenticatedGuard } from 'src/auth/guards/authenticated.guard';
import { UserService } from 'src/database/entities/user/user.service';

AdminJS.registerAdapter({
    Resource: AdminJSTypeorm.Resource,
    Database: AdminJSTypeorm.Database,
});

type CurrentAdmin = {
    /**
     * Admin has one required field which is an email
     */
    email: string;
    /**
     * Optional title/role of an admin - this will be presented below the email
     */
    title?: string;
    /**
     * Optional url for an avatar photo
     */
    avatarUrl?: string;
    /**
     * Id of your admin user
     */
    id?: string;
    /**
     * Also you can put as many other fields to it as you like.
     */
    [key: string]: any;
};

const DEFAULT_ADMIN: CurrentAdmin = {
    email: 'admin@example.com',
    password: 'password',
    title: 'Admin',
    avatarUrl: 'https://i.imgur.com/4by23BO.png',
    id: '123',
};

const authenticate = async (email, password) => {
    // <- Here I want to use UserService to check if user exists in database

    if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
        return DEFAULT_ADMIN;
    }
    return null;
};



@Module({
    imports: [
        AdminModule.createAdmin({
            adminJsOptions: {
                rootPath: '/admin',
                resources: [
                    UserResource,
                    ChannelResource
                ],
                componentLoader: componentLoader,
                locale: AdminPanelLocale,
                branding: {
                    logo: 'https://i.imgur.com/4by23BO.png',
                    companyName: 'Polskie Legendy Apex',
                    withMadeWithLove: false,
                    favicon: 'https://i.imgur.com/u4DEkaz.png',
                },
            },
            auth: {
                authenticate: authenticate,
                cookieName: 'adminjs',
                cookiePassword: 'secret-cookie',
            },
            sessionOptions: {
                resave: false,
                saveUninitialized: false,
                secret: 'super-secret',
            }
        }),
    ],
    controllers: [],
    providers: [],
    exports: [],
})
export class AdminPanelModule {}

I want to use my user service and all my TypeORM stuff to put some authorization stuff going on in the authentication function. But I just don't know how could I use Nest.js things outside of module declaration.

Can you help me? ❤️‍🩹

runic iron
#

Does that AdminModule have a createAdminAsync method where you can inject the Typeorm repository?

#

Otherwise, what you're asking for isn't really a possibility

narrow mural
#

There is a possibility of doing that!

#

I'll modify the code and let you know

narrow mural
# runic iron Does that `AdminModule` have a `createAdminAsync` method where you can inject th...

Ok, so here's async version:

import { Module } from '@nestjs/common';
import { AdminModule } from '@adminjs/nestjs';
import { UserEntity } from 'src/database/entities/user/user.entity';

import * as AdminJSTypeorm from '@adminjs/typeorm';
import AdminJS from 'adminjs';
import theme from '@adminjs/design-system';
import { ChannelEntity } from 'src/database/entities/channel/channel.entity';
import { UserResource } from './resources/user.resource';
import { AdminPanelLocale } from './admin-panel.locale';
import { ChannelResource } from './resources/channel.resource';
import { componentLoader } from './components/components';
import { AuthenticatedGuard } from 'src/auth/guards/authenticated.guard';
import { UserService } from 'src/database/entities/user/user.service';

AdminJS.registerAdapter({
    Resource: AdminJSTypeorm.Resource,
    Database: AdminJSTypeorm.Database,
});

const DEFAULT_ADMIN = {
    email: 'admin@example.com',
    password: 'password',
    title: 'Admin',
    avatarUrl: 'https://i.imgur.com/4by23BO.png',
    id: '123',
};

const authenticate = async (email, password) => {
    // <- Here I want to use UserService to check if user exists in database

    if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
        return DEFAULT_ADMIN;
    }
    return null;
};



@Module({
    imports: [
        AdminModule.createAdminAsync({
            //   imports: [TypeOrmModule.forFeature([UserEntity])],
            useFactory: async (/*userService: UserService*/) => ({
                adminJsOptions: {
                    rootPath: '/admin',
                    resources: [
                        UserResource,
                        ChannelResource
                    ],
                    componentLoader: componentLoader,
                    locale: AdminPanelLocale,
                    branding: {
                        logo: 'https://i.imgur.com/4by23BO.png',
                        companyName: 'Polskie Legendy Apex',
                        withMadeWithLove: false,
                        favicon: 'https://i.imgur.com/u4DEkaz.png',
                    },
                },
                auth: {
                    authenticate: authenticate,
                    cookieName: 'adminjs',
                    cookiePassword: 'secret-cookie',
                },
                sessionOptions: {
                    resave: false,
                    saveUninitialized: false,
                    secret: 'super-secret',
                }
            }),
            // inject: [UserService],
        }),
    ],
    controllers: [],
    providers: [],
    exports: [],
})
export class AdminPanelModule {}
#

So if I understand correctly, now I'm able to pass everything I want into factory and then use it and it's going to be passed from the superior module?

runic iron
#

You need to specify what to pass viathe inject property

narrow mural
# runic iron You need to specify what to pass viathe `inject` property

I did it like this:

@Module({
    imports: [
        AdminModule.createAdminAsync({
            //   imports: [TypeOrmModule.forFeature([UserEntity])],
            useFactory: async (UserService: UserService) => ({
                adminJsOptions: {
                    rootPath: '/admin',
                    resources: [
                        UserResource,
                        ChannelResource
                    ],
                    componentLoader: componentLoader,
                    locale: AdminPanelLocale,
                    branding: {
                        logo: 'https://i.imgur.com/4by23BO.png',
                        companyName: 'PLA Admin Panel',
                        withMadeWithLove: false,
                        favicon: 'https://i.imgur.com/4by23BO.png',
                    },
                },
                auth: {
                    authenticate: authenticate,
                    cookieName: 'adminjs',
                    cookiePassword: 'secret-cookie',
                },
                sessionOptions: {
                    resave: false,
                    saveUninitialized: false,
                    secret: 'super-secret',
                },
                versionSettings: {
                    admin: true,
                    app: '1.0.0',
                }
            }),
            inject: [UserService],
        }),
    ],
    controllers: [],
    providers: [],
    exports: [],
})
export class AdminPanelModule {}```
#

But I get an error:

the UserService (?, DiscordService). Please make 
sure that the argument UserEntityRepository at index [0] is available in the AdminPanelModule context.

Potential solutions:
- Is AdminPanelModule a valid NestJS module?     
- If UserEntityRepository is a provider, is it part of the current AdminPanelModule?
- If UserEntityRepository is exported from a separate @Module, is that module imported within AdminPanelModule?
  @Module({
    imports: [ /* the Module containing UserEntityRepository */ ]
  })```
runic iron
#

Does your UserModule usually have UserService in the exports array?

narrow mural
#
@Module({
    providers: [
        UserService,
    ],
    exports: [
        UserService,
    ],
    imports: [
        DiscordModule,
        DatabaseModule,
        TypeOrmModule.forFeature([UserEntity]),
    ],
})
export class UserModule {}```
runic iron
#

If so, then add imports: [UserModule] to the AdminModule's options

narrow mural
#

but I LITERALLY TRIED THAT

#

why does it work when you tell me

runic iron
narrow mural
#

thank you @runic iron

#

I'll try to use it then