KevinEdry / nestjs-trpc

Providing native support for decorators and implement an opinionated approach that aligns with NestJS conventions.
https://NestJS-tRPC.io
MIT License
61 stars 6 forks source link

The "PLACEHOLDER_DO_NOT_REMOVE" string is not being replaced in the generated server file. #35

Open BenjaminSpoiden opened 1 week ago

BenjaminSpoiden commented 1 week ago

Hello, I'm facing an issue with the implementation of tRPC with NestJS.

I've followed the setup in docs and it generates a server.ts file as expected, but when referencing a test endpoint inside a .router.ts file, the content of the Query in server.ts is not being updated accordingly.

Am I misconfiguring something?

Here are the files: @generated/server.ts

import { initTRPC } from "@trpc/server";
import { z } from "zod";

const t = initTRPC.create();
const publicProcedure = t.procedure;

const appRouter = t.router({
  test: t.router({
    getHello: publicProcedure.input(z.object({
      name: z.string()
    })).output(z.object({
      name: z.string()
    })).query(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any)
  })
});
export type AppRouter = typeof appRouter;

app.router.ts

import { Get, Req } from '@nestjs/common';
import { AppService } from './app.service';
import { AuthGuard } from './auth/auth.guard';
import { Request } from 'express';
import { Input, Middlewares, Query, Router } from 'nestjs-trpc';
import { z } from 'zod';

@Router({ alias: 'test' }) 
export class AppRouter {
  constructor(private readonly appService: AppService) {}

  @Query({ 
    input: z.object({
      name: z.string()
    }),
    output: z.object({
      name: z.string()
    })
  })
  async getHello(@Input('name') name: string) {
    return {
      name
    }
  }
}

app.module.ts

import { Module } from '@nestjs/common';
import { TRPCModule } from 'nestjs-trpc';
import { AppRouter } from './app.router';
import { AppService } from './app.service';
import { EnvModule } from './env/env.module';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { DrizzleModule } from './drizzle/drizzle.module';
import { UploadModule } from './upload/upload.module';
import { SongsModule } from './songs/songs.module';
import { RedisClientModule } from './redis-client/redis-client.module';
import { RoomsModule } from './rooms/rooms.module';
import { AppContext } from './app.context';

@Module({
  imports: [
    TRPCModule.forRoot({
      autoSchemaFile: './src/@generated',
      context: AppContext
    }),
    EnvModule,
    AuthModule,
    UsersModule,
    DrizzleModule,
    UploadModule,
    SongsModule,
    RedisClientModule,
    RoomsModule,
  ],
  providers: [AppService, AppRouter, AppContext],
})
export class AppModule {}

Thank you!

KevinEdry commented 1 week ago

Hey @BenjaminSpoiden, Thanks for opening a new issue, I will try to clarify. @generated/server.ts is ment for type inference only during compile-time, it's there to get the trpc dx with typescript support and you shouldn't expect logic to be there, all of the actual logic is done by the adapter when it applies your actual router methods to the trpc sdk during run-time.

Since the client-side trpc does not need the actual implementation details of the when referencing the router types, having placeholders is enough to trick it to reference the actual endpoints during run-time and still provide the great e2e dx type-safety experience.

If you try to actually reach that endpoint, it should work, and you are welcome to open a PR that will update the generated file to include an explanation about the placeholders there for future developers.

Does this make sense?

BenjaminSpoiden commented 1 week ago

Thanks for your quick response!

I'm not too sure, for example when I try to test an endpoint out inside a next.js project, I get this error:

Server  Error: No "query"-procedure on path "test.getHello"
    at TRPCClientError.from (TRPCClientError-38f9a32a.mjs:27:20)
    at <anonymous> (httpBatchLink-d0f9eac9.mjs:189:60)
    at resolveErrorDev (react-server-dom-turbopack-client.browser.development.js:1785:63)
    at processFullStringRow (react-server-dom-turbopack-client.browser.development.js:2062:17)
    at processFullBinaryRow (react-server-dom-turbopack-client.browser.development.js:2050:7)
    at progress (react-server-dom-turbopack-client.browser.development.js:2253:17)

Here's my trpc.ts file:

import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
import { AppRouter } from "@server/@generated/server";

export const trpc = createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: `${process.env.NEXT_PUBLIC_NESTJS_SERVER}/trpc`,
    }),
  ],
});

and the use case:

import { UserButton } from "@clerk/nextjs";
import { SocketTest } from "@frontend/components/Socket";
import { trpc } from "./trpc";

export default async function Home() {
  const test = await trpc.test.getHello.query({ name: 'test' })

  console.log('test', test)
  return (
    <div>
      <UserButton />
      <SocketTest />
    </div>
  );
}

For a bit of context, this a monorepo using pnpm with Next.js 15 on the frontend side.

KevinEdry commented 1 week ago

That should have worked, I am not not seeing anything out of the ordinary with the code you shared. Have you tried reaching the trpc endpoints via Postman? it should be available at GET http://localhost:{NestJS-Port}/trpc/test.getHello.

BenjaminSpoiden commented 1 week ago

Oh yeah Its working fine with Postman, I can access the endpoint. Maybe it has something to do with the last version of Next.js?

KevinEdry commented 1 week ago

Maybe, if you are able to hit the endpoint from Postman it seems that it is a problem with the consumption of the API in the client. For troubleshooting, I'd recommend you either check if theres a version mismatch between your trpc installation in Nestjs and Nextjs, or check the Next.js example in the repo for implementation details, while it is in Next.js 13, I don't think there were breaking changes.