So I was following this tutorial https://www.tomray.dev/nestjs-nextjs-trpc
and after finishing NestJS part discovered that my code not working and throwing this error while my code looked identical to the one from the tutorial.
[Nest] 94625 - 10/01/2024, 11:43:53 AM ERROR [ExceptionHandler] Nest can't resolve dependencies of the TrpcRouter (?). Please make sure that the argument Function at index [0] is available in the TrpcModule context.
Potential solutions:
- Is TrpcModule a valid NestJS module?
- If Function is a provider, is it part of the current TrpcModule?
- If Function is exported from a separate @Module, is that module imported within TrpcModule?
@Module({
imports: [ /* the Module containing Function */ ]
})
Here's the code of that module:
import { Injectable, type INestApplication } from '@nestjs/common';
import type { TrpcService } from './trpc.service';
import { z } from 'zod';
import * as trpcExpress from '@trpc/server/adapters/express';
@Injectable()
export class TrpcRouter {
constructor(private readonly trpc: TrpcService) {}
appRouter = this.trpc.router({
hello: this.trpc.procedure
.input(z.object({ name: z.string().optional() }))
.query(({ input }) => {
return `Hello ${input.name ? input.name : 'World'}!`;
}),
});
async applyMiddleware(app: INestApplication) {
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({ router: this.appRouter }),
);
}
}
export type AppRouter = TrpcRouter['appRouter'];
After trying different things I discovered that thing that lead to this error is the fact that I'm importing type, not a whole module at this line
import type { TrpcService } from './trpc.service';
and after changing it to original tutorial version
import { TrpcService } from './trpc.service';
it started working properly.
But I'm using it only as type, not as value
constructor(private readonly trpc: TrpcService) {}
So the question is - why this happens? Why it throws error when I'm only importing type and not the value? I suppose that's how NestJS works, or am I missing something in how typescript handles it?
PS: there are other questions similar to this, but none of them talking about this particular differences - type import/regular import
PPS: searching for type imports I found this issue which may be the answer to my question. Reading it now https://github.com/nestjs/nest/issues/5421
If you still want to use import type
for some reason. You can use other way define provider for container Nest's DI system.
When define provider
providers: [
{
provider: "TrpcSvc", // use string or symbol at here
useClass: TrpcService
}
]
...
import type { TrpcService } from './trpc.service';
// Inject
constructor(@Inject("TrpcSvc") private readonly trpc: TrpcService) {}
The most important is way that you import provider in module. If you use default import provider: [TrpcService]
, it mean you add class TrpcService
to container of Nest's DI system with key and value is class TrpcService
. It like this syntax
import { TrpcService } ....
...
providers: [
{
provider: TrpcService,
useClass: TrpcService
}
]
In opposite, NestJS don't support define type
for key of providers
. It is code NestJS define module options.
...
/**
* Interface defining the property object that describes the module.
*
* @see [Modules](https://docs.nestjs.com/modules)
*
* @publicApi
*/
export interface ModuleMetadata {
...
/**
* Optional list of providers that will be instantiated by the Nest injector
* and that may be shared at least across this module.
*/
providers?: Provider[];
...
}
...
// For class
export interface Type<T = any> extends Function {
new (...args: any[]): T;
}
...
export type Provider<T = any> = Type<any> | ClassProvider<T> | ValueProvider<T> | FactoryProvider<T> | ExistingProvider<T>;
Currently, provider only accept value
not type
.