#How can i access my NestApplication in a controller

197 messages · Page 1 of 1 (latest)

prisma blade
#

How can i access my NestApplication in a controller?

#
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ApplicationOptions } from "./types";
import { NestFastifyApplication, FastifyAdapter } from "@nestjs/platform-fastify";
import { Database } from "A Custom package built on top of sequelize";

export class Application {
    server: NestFastifyApplication;
    constructor(options: ApplicationOptions) {
        this._init();
    }

    async _init() {
        const server = await NestFactory.create<NestFastifyApplication>(
            Module,
            new FastifyAdapter()
        );
        this.server = server;
        // Custom property that can be accessed by controllers
        this.server.db = new Database(
            
    }

    start() {
        this.server.listen(880, () =>
            console.log("server started on port 880")
        );
    }
}
tropic marlin
#

You can’t

prisma blade
tropic marlin
#

using custom providers? I'm not sure if I follow

prisma blade
tropic marlin
#

the docs covers custom providers very well

#

what are you trying to achieve here? I didn't get

prisma blade
#

I have a configurable module

import { Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { DatabaseModule } from './modules/database/database.module';
@Module({
  imports: [],
  controllers: [],
  providers: [DatabaseModule.register({
    dialect: 'sqlite',
    storage: './blogifi.db',
  })],
})
export class BlogifiServerModule {}

I want to pass the options given to BlogifiServerModule during NestFactory.create to DatabaseModule

#

Is that posible

#

Or i register it directly from NestApplication

#

@tropic marlinyou there?

tropic marlin
#

yep

prisma blade
#

so.. what can i do?

#

I think this does my work

prisma blade
tropic marlin
#

you cannot pass options to modules

#

I mean, you can supply them via custom providers
But its depends on where you want to access those options

#

just learn about custom providers and async providers
That's all you have to know to see if what you trying to do is feasible or not

prisma blade
tropic marlin
#

I got you

#

you want to supply some data from main.ts

prisma blade
#

yes

#
        this.server.get(DatabaseModule.register)

I think this will work but i get a types error

tropic marlin
#

I don't think that that's feasible
might be in a hacky way

prisma blade
#

maybe

tropic marlin
#

as we can't define custom providers from main.ts

prisma blade
#

so.. what am i left with?

#
import { Global, Module } from '@nestjs/common';
import { ConfigurableModuleClass } from './database.definition';
import { DatabaseService } from './database.service';

@Global()
@Module({
  providers: [DatabaseService],
  exports: [DatabaseService],
})
export class DatabaseModule extends ConfigurableModuleClass {}
import { ConfigurableModuleBuilder } from '@nestjs/common';
import { Options } from '@sequelize/core';


export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
    new ConfigurableModuleBuilder<Options>({
        moduleName: 'Database',
    })
        .build();
import { BlogifiDatabase } from '@blogifi/database';
import { Inject, Injectable } from '@nestjs/common';
import { Options } from '@sequelize/core';
import { MODULE_OPTIONS_TOKEN } from './database.definition';

@Injectable()
export class DatabaseService {
    database: BlogifiDatabase;
    constructor(
        @Inject(MODULE_OPTIONS_TOKEN)
        private readonly options: Options,
    ) { 
        this.database = new BlogifiDatabase(options as any);
    }
}
#

This is my Database Module which requires those options

#

@tropic marlin Would you prefer to join my live share session for seeing the whole codebase?

tropic marlin
prisma blade
#

ah, sorry ;-;

#

apologies

tropic marlin
#

no problem
I work remotely and we use discord, that's why I need to have the status 'online'
I'll see if I follow what you're trying to do later

prisma blade
#
        this.server.get(DatabaseModule).register(this.options.database);

This is what i was doing, but it does not seem to work

tropic marlin
#

show us the whole context around that line

solar raptor
#

Question: why did a dynamic missing not satisfy your use case? What's the need to set values from the main?

prisma blade
solar raptor
#

I'm not really sure I follow. I mean, you could make your AppModule take in options via a static method, just like a dynamic module, and pass them on to the database module

prisma blade
solar raptor
#

I'll need to be back at my computer to give an example, but basically it would be a dynamic module passing input data to another dynamic module

prisma blade
#

when you get back to the computer just provide me an example

tropic marlin
solar raptor
#

Just a simple static register(options) that returns the new module object. No need for an async variant

prisma blade
# tropic marlin turn that `AppModule` into a dynamic module, first
import { Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { DatabaseModule } from './modules/database/database.module';
import { ConfigModule } from '@nestjs/config';
import config from './config';
import { ConfigurableModuleClass } from './app.definition';
import { AuthModule } from './modules/auth/auth.module';


@Module({
  imports: [ConfigModule.forRoot({
    isGlobal: true,
    load: [config]
  }), AuthModule],
  controllers: [],
  providers: [DatabaseModule],
})
export class BlogifiServerModule extends ConfigurableModuleClass {}
import { ConfigurableModuleBuilder } from '@nestjs/common';
import { Options } from '@sequelize/core';


export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
    new ConfigurableModuleBuilder<Options>({
        moduleName: 'BlogifiApp',
    })
        .build();
#

Done

solar raptor
prisma blade
#

how am i gonna initialize the DatabaseModule via the config provided?

solar raptor
#

What? I thought you wanted to pass config from main?

prisma blade
#

yes

#

how am i gonna access the config

solar raptor
#

DatabaseModule is also a dynamic module, right?

prisma blade
#

yeah

solar raptor
#

So you pass the config to AppModule in main and then AppModule passes the config to DatabaseModule

#

It's just nested static method calls

prisma blade
#

how is AppModule gonna pass the config to DatabaseModule

solar raptor
#

Via DatabaseModule's register method

prisma blade
#

where am i gonna call the register method while having access to the config?

solar raptor
#

When you import DatabaseModule I'm AppModule

#

Do you get how module imports and dynamic modules work here?

prisma blade
# solar raptor When you import `DatabaseModule` I'm `AppModule`
import { Inject, Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { DatabaseModule } from './modules/database/database.module';
import { ConfigModule } from '@nestjs/config';
import config from './config';
import { ConfigurableModuleClass } from './app.definition';
import { AuthModule } from './modules/auth/auth.module';
import { MODULE_OPTIONS_TOKEN } from './app.definition';
import { BlogifiServerOptions } from './types';

@Module({
  imports: [ConfigModule.forRoot({
    isGlobal: true,
    load: [config]
  }), AuthModule, DatabaseModule],
  controllers: [],
  providers: [],
})
export class BlogifiServerModule extends ConfigurableModuleClass {
  constructor(
    @Inject(MODULE_OPTIONS_TOKEN)
    private readonly options: BlogifiServerOptions,
  ) {
    super();
    DatabaseModule.register(options.database);
  }
}
#

Like this?

#

BlogifiServerModule is the AppModule

tropic marlin
#

you need to learn a bit on dynamic modules and modules in general

#

just calling DatabaseModule.register doesn't mean that it got registered

#

the return of DatabaseModule.register() is a dynamic module
Like any nestjs module, it must be in the imports array somewhere

prisma blade
#

do i need call register in imports?

tropic marlin
#

you need to put the output of .register() in the imports

#

that's how you import nestjs modules

prisma blade
#

i didnt get that

tropic marlin
#

did you read the docs?

prisma blade
tropic marlin
#

it won't mention every thing you could build, of course
just the building blocks

#

see, the output of .register() is in the imports array

prisma blade
#

but what should i keep the output to be?

tropic marlin
#

I meant the return of .register() method

#

it should be in the imports array otherwise your dynamic module won't be imported, you know

prisma blade
tropic marlin
#

right
show me your root module's code and I'll give you a code example on what you could

prisma blade
#
import { Inject, Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { DatabaseModule } from './modules/database/database.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import config from './config';
import { AuthModule } from './modules/auth/auth.module';
import { AppConfigOptions, BlogifiServerOptions } from './types';

@Module({
  imports: [ConfigModule.forRoot({
    isGlobal: true,
    load: [config]
  }), AuthModule, DatabaseModule.register({
    
  })],
  controllers: [],
  providers: [],
})
export class BlogifiServerModule {
  constructor(private readonly configService: ConfigService) {
    DatabaseModule.register({
      ...this.configService.get('database')
    })
  }
}

This is my updated code

tropic marlin
#

is this the root module?

prisma blade
#

yes

#

the app.module

tropic marlin
#

wait, you only need to use ConfigService in order to create that DatabaseModule?

#

or do you need some value from main.ts as well?

prisma blade
#

i removed the main.ts thing

tropic marlin
#

than it's easier

prisma blade
#

Also, If i am using the Database module in another module i wont have to register it in imports right?

tropic marlin
#

you need to register it in some module
but only once

prisma blade
#

yeah i am doing it in the main app module

prisma blade
#

this is the DatabaseModule

tropic marlin
#

right so you'll need to use the async version of it

#

in order to initialize its providers with data coming from ConfigService

#

so instead of imports: [DatabaseModule] you'll have

imports: [DatabaseModule.registerAsync({ ... })]
#

again, just follow the docs

#

in there you'll have inject: [ConfigService]

#

and then

// ...
useFactory: (configService: ConfigService) => configService.get('database')
// ...

pretty easy

prisma blade
tropic marlin
#

nope

prisma blade
#

aight

tropic marlin
#

like I said many times, you need to have the return of .register() in some imports array otherwise it's useless

#

instead of 'register' you could see it as 'createDynamicModule'

prisma blade
#

[Nest] 6021 - 20/10/2022, 22:39:21 ERROR [ExceptionHandler] Cannot read properties of undefined (reading 'get')
TypeError: Cannot read properties of undefined (reading 'get')
at InstanceWrapper.useFactory [as metatype] (/Users/gaurish/Desktop/Coding/blogifi/src/file:/Users/gaurish/Desktop/Coding/blogifi/src/app.module.ts:16:65)

tropic marlin
#

show us how your root module looks like rn

prisma blade
#
import { Inject, Module } from '@nestjs/common';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
import { DatabaseModule } from './modules/database/database.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import config from './config';
import { AuthModule } from './modules/auth/auth.module';
import { AppConfigOptions, BlogifiServerOptions } from './types';
import { AuthController } from './controllers/auth/auth.controller';

@Module({
  imports: [ConfigModule.forRoot({
    isGlobal: true,
    load: [config]
  }), AuthModule, DatabaseModule.registerAsync({
    useFactory: (configService: ConfigService) => configService.get('database')
  })],
  controllers: [AuthController],
  providers: [],
})
export class BlogifiServerModule {} 
tropic marlin
#

that errors is due to configService being undefined, somehow

#

you missed the inject

prisma blade
#

inject?

#
DatabaseModule.registerAsync({
    inject: [ConfigService],
    useFactory: (configService: ConfigService) => configService.get('database')
  })
tropic marlin
#

yep

#

otherwise nestjs won't know that it should inject something in there useFactory

#

isn't magical

#

haha

prisma blade
#
[Nest] 6041  - 20/10/2022, 22:41:31   ERROR [ExceptionHandler] Nest can't resolve dependencies of the DatabaseService (?). Please make sure that the argument DATABASE_MODULE_OPTIONS at index [0] is available in the DatabaseModule context.
#

bruh moment

#

wait

#

do i really need DatabaseModule to be a dynamic module

#

since i can just use the configService

tropic marlin
#

if you want supply data coming from some provider in it, it must be async and dynamic

#

you can't inject providers in modules

#

not like we have for @Injectable()

prisma blade
#

DatabaseModule has DatabaseService

#
import { Global, Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigurableModuleClass } from './database.definition';
import { DatabaseService } from './database.service';

@Global()
@Module({
  imports: [ConfigService],
  providers: [DatabaseService],
  exports: [DatabaseService],
})
export class DatabaseModule {}
solar raptor
#

Okay, let's take a step backand get this straightend out

prisma blade
#
import { BlogifiDatabase } from '@blogifi/database';
import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Options } from '@sequelize/core';
import { MODULE_OPTIONS_TOKEN } from './database.definition';

@Injectable()
export class DatabaseService {
    database: BlogifiDatabase;
    constructor(
        private configService: ConfigService
    ) { 
        const options = this.configService.get<Options>('database');
        this.database = new BlogifiDatabase(options);
    }
}
tropic marlin
#

that will work but looks odd from nestjs point of view

solar raptor
#

You just need a way to pass database credentials to your database service, right? No more passing values from main.ts or elsewhere in the app, you're just gonna let the ConfigService be used and read from the .env file, correct?

prisma blade
#

Yes

solar raptor
#

What is BlogifiDatabase and how is it different than the DatabaseService? I see you instantiating it in the DatabaseService, but I generally try to stay away from new inside of constructors if I can help it. Would it be better/preferable to just pass an instance to the DatabaseService?

prisma blade
#

this does most of my work

solar raptor
#

My question still stands, would it be better/preferable to pass the instance of BlogifiDatabase rather than instantiating it in the DatabaseService in your opinion

#

Or better yet, what's the reason for the DatabaseService? What does it do for you?

prisma blade
#

yes, It would be better to pass the instance of BlogifiDatabase but the thing is i wanted to be accessible in every controller/module

#

and that's the main reason i created DatabaseService

solar raptor
#

Okay, so why have this wrapper in the first place? Why not just have a custom provider that links an injection token to the instance?

{
  provide: 'BlogIfiDatabase',
  inject: [ConfigService],
  useFactory: (config: ConfigService) => {
    return new BlogifiDatabase(config.get('database');
  }
}
#

Now it's injectable with @Inject('BlogifiDatabase') private readonly database: BlogifiDatabase

#

Add the above to the providers array of DatabaseModule and add exports: ['BlogifiDatabase'] and then just add imports: [DatabaseModule] top modules where you need the database

#

Bada-bing bada-boom it's all hooked up and gets values from the ConfigService, nothing is hard coded and everything just works™️

prisma blade
#

Where am i gonna do @Inject('BlogifiDatabase') private readonly database: BlogifiDatabase

solar raptor
#

In any service that you need to use the database

prisma blade
#

and for controllers?

solar raptor
#

Do you use the database in your controllers?

prisma blade
#

yes

solar raptor
#

Then do the same thing. It's just a way to set metadata for Nest to read

#

Though, I would suggest not having business logic in the controllers, but that's my opinion

prisma blade
#

For services?

#
import { Blog } from '@blogifi/database/lib/types';
import { Injectable } from '@nestjs/common';
import { DatabaseService } from 'src/modules/database/database.service';

@Injectable()
export class APIService {
    constructor(private readonly databaseService: DatabaseService) { }
}
#

I have this service

#

that utilizes the DatabaseService

solar raptor
#

Right, and I showed a way that didn't even need the DatabaseService, it just uses the BlogifiDatabase directly via a custom provider

#

Was that not clear?

prisma blade
#

it is

#

But what am i gonna do to the Injectable decorator

solar raptor
prisma blade
#
import { BlogifiDatabase } from '@blogifi/database';
import { Blog } from '@blogifi/database/lib/types';
import { Inject, Injectable } from '@nestjs/common';
import { DatabaseService } from 'src/modules/database/database.service';

@Injectable()
export class APIService {
    private readonly database: BlogifiDatabase;
    constructor(
        @Inject('BlogifiDatabase') database: BlogifiDatabase
    ) {
        this.database = database;
    }
}
#

So this should work right?

solar raptor
#

You don't need the this.database = database, just use @Inject('BlogifiDatabase') private readonly database: BlogifiDatabase)

#

The private readonly is a shorthand that TS compiles to the this.database = database for you, less code for you to worry about

prisma blade
#
 constructor(
        @Inject('BlogifiDatabase') private readonly database: BlogifiDatabase
    ) { }
#

perfect right

solar raptor
#

Now you can access this.database inside the class

prisma blade
#

and i am gonna do the same for controllers?

solar raptor
#

Why wouldn't you?

prisma blade
#

in their constructor

solar raptor
#

Why would you do something different for controllers?

prisma blade
#

i mean just casually asking

#

alright then

#

thanks a lot dude!

prisma blade
#

@solar raptor yo sorry for the ping i'm facing some weird problem this time

#
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
import { Controller, Get, Post, Patch, Delete, Body, Res, Inject, Param } from '@nestjs/common';
import { CreateBlogBodyType, CreateBlogRequest, GetAndDeleteBlogRequest, UpdateBlogRequest } from './api.schema';
import { APIService } from './api.service';
import { BlogifiDatabase } from '@blogifi/database';


@Controller({ path: '/api/v1' })
export class APIController {

    constructor(
        @Inject('BlogifiDatabase') private readonly database: BlogifiDatabase
    ) { }

    @Get('/blogs')
    async getAllBlogsHandler() {
        const blogs = await this.database.getBlogs()
        return blogs;
    }
}
#

I have this simple controller which uses the database, It does reply the request for some reason

#

i keep getting a null in response body

#

in db logs the statement is executed

solar raptor
#

Does the statement actually return data?

prisma blade
#

no i dont think so

#

i did console.log in function to test

#

on request it didnt log

solar raptor
#

So a null response makes sense, right?

prisma blade
#

yeah

#

Also

#

Does the request method matter for @nestjs/passport

#
import { Controller, Get, Inject, Request, UseGuards } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { LocalAuthGuard } from "../../modules/auth/local.guard";

@Controller({ path: "/auth"})
export class AuthController {

    @UseGuards(LocalAuthGuard)
    @Get("/session")
    async loginHandler(@Request() req) {
        return req.user
    }
}

I have this thing but it does not create the session

solar raptor
#

Do you have it set up to use sessions?

prisma blade
#

Yes