sachinraja / trpc-playground

playground for running tRPC queries in the browser
MIT License
281 stars 19 forks source link

TRPC Playground with Fastify: Route GET:/api/trpc-playground not found #50

Closed CrispenGari closed 9 months ago

CrispenGari commented 9 months ago

I'm trying to integrate trpc-playground with fastify using the getFastifyPlugin() but when I visit http://localhost:3001/api/trpc-playground i'm getting the following error:

{"message":"Route GET:/api/trpc-playground not found","error":"Not Found","statusCode":404}

Here is my implementation

 .....
import { getFastifyPlugin } from "trpc-playground/handlers/fastify";

const PORT: any = process.env.PORT || 3001;
const HOST =
  process.env.NODE_ENV === "production"
    ? "0.0.0.0"
    : "localhost" || "127.0.0.1";
const TPRC_API_ENDPOINT = "/api/trpc";
const TRPC_PLAYGROUND_ENDPOINT = "/api/trpc-playground";

(async () => {
  const fastify = Fastify({
    logger: true,
    ignoreTrailingSlash: true,
    maxParamLength: 5000,
  });
  fastify.register(fastifyTRPCPlugin, {
    prefix: TPRC_API_ENDPOINT,
    trpcOptions: { router: appRouter, createContext },
    useWSS: true,
  });
  fastify.register(
    await getFastifyPlugin({
      router: appRouter,
      trpcApiEndpoint: TPRC_API_ENDPOINT,
      playgroundEndpoint: TRPC_PLAYGROUND_ENDPOINT,
      request: {
        superjson: true,
      },
    })
  );

})();

Maybe it's because i don't know how to use this plugin correctly with fastify. What might be possibly the problem in my code?

MatsDK commented 9 months ago

Your issue might be related to #28.

CrispenGari commented 9 months ago

@MatsDK Thanks for your response. I've tried to follow that related issues #28 . It works but i can't make any queries or mutations in the playground I'm getting the error saying:

[ReferenceError: query is not defined]

What maybe possibly the issue. I modified my server to:

  fastify.register(
    await getFastifyPlugin({
      router: appRouter,
      trpcApiEndpoint: TPRC_API_ENDPOINT,
      playgroundEndpoint: TRPC_PLAYGROUND_ENDPOINT,
      request: {
        superjson: true,
      },
    }),
    { prefix: TRPC_PLAYGROUND_ENDPOINT }
  );
MatsDK commented 9 months ago

The fastify plugin seems to be working for me, can you share your package.json, router setup.

CrispenGari commented 9 months ago

The fastify plugin seems to be working for me, can you share your package.json, router setup.

Here is my package.json

{
  "name": "@dispatch/api",
  "version": "1.0.0",
  "description": "This is the api server for the startup dispatch.",
  "main": "src/index.ts",
  "author": "crispengari",
  "license": "MIT",
  "scripts": {
    "start": "ts-node-dev --respawn --transpile-only src/index.ts"
  },
  "dependencies": {
    "@crispengari/utils": "^1.0.1",
    "@fastify/cors": "^8.3.0",
    "@fastify/websocket": "^8.2.0",
    "@prisma/client": "^5.3.1",
    "@trpc/server": "^10.38.3",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "fastify": "^4.23.2",
    "ip": "^1.1.8",
    "jsonwebtoken": "^9.0.2",
    "node-env-types": "^1.0.6",
    "nodemailer": "^6.9.5",
    "qrcode": "^1.5.3",
    "superjson": "^1.13.1",
    "trpc-playground": "^1.0.4",
    "zod": "^3.22.2"
  },
  "devDependencies": {
    "@types/bcryptjs": "^2.4.4",
    "@types/ip": "^1.1.0",
    "@types/jsonwebtoken": "^9.0.3",
    "@types/node": "^20.6.1",
    "@types/nodemailer": "^6.4.10",
    "@types/qrcode": "^1.5.2",
    "ts-node-dev": "^2.0.0"
  }
}

Here the appRouter

import { router } from "../trpc/trpc";
import { helloRouter } from "./hello/hello.router";
import { userRouter } from "./user/user.routes";

export const appRouter = router({
  hello: helloRouter,
  user: userRouter,
});

export type AppRouter = typeof appRouter;

Here is the hello.routes.ts file:

import { z } from "zod";
import { publicProcedure, router } from "../../trpc/trpc";
import EventEmitter from "events";
import { Events } from "../../constants";
import { observable } from "@trpc/server/observable";

const ee = new EventEmitter({
  captureRejections: true,
});

export const helloRouter = router({
  greeting: publicProcedure
    .input(
      z.object({
        name: z
          .string()
          .min(3, { message: "minimum of 3 characters" })
          .max(10, { message: "maximum of 10 characters" }),
      })
    )
    .output(z.object({ message: z.string() }))
    .query(({ ctx, input: { name } }) => {
      return {
        message: `Hello ${name}`,
      };
    }),
  fromTRPC: publicProcedure.query(({ ctx }) => "Hello from TRPC"),
  hi: publicProcedure
    .input(
      z.object({
        message: z.string(),
        name: z.string(),
      })
    )
    .mutation(({ input: { message, name } }) => {
      ee.emit(Events.ON_HI, {
        name,
        message,
      });
      return {
        message,
      };
    }),
  onHi: publicProcedure.subscription(async ({}) => {
    return observable<{ name: string; message: string }>((emit) => {
      const handler = (res: { name: string; message: string }) => {
        emit.next(res);
      };
      ee.on(Events.ON_HI, handler);
      return () => {
        ee.off(Events.ON_HI, handler);
      };
    });
  }),
});

The server implementation is as follows:

// src/index.ts

import "dotenv/config";
import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
import ip from "ip";
import _ from "node-env-types";
import { createContext } from "./context/context";
export { type AppRouter } from "./routes/app.routes";
import { appRouter } from "./routes/app.routes";
import Fastify from "fastify";
import cors from "@fastify/cors";
import ws from "@fastify/websocket";
import { getFastifyPlugin } from "trpc-playground/handlers/fastify";
export type { User } from "@prisma/client";

_();

const PORT: any = process.env.PORT || 3001;
const HOST =
  process.env.NODE_ENV === "production"
    ? "0.0.0.0"
    : "localhost" || "127.0.0.1";
const TPRC_API_ENDPOINT = "/api/trpc";
const TRPC_PLAYGROUND_ENDPOINT = "/api/trpc-playground";

(async () => {
  const fastify = Fastify({
    logger: false,
    ignoreTrailingSlash: true,
    maxParamLength: 5000,
  });

  fastify.register(ws);
  fastify.register(cors, {
    credentials: true,
    origin: ["http://localhost:3000"],
  });

  fastify.register(fastifyTRPCPlugin, {
    prefix: TPRC_API_ENDPOINT,
    trpcOptions: { router: appRouter, createContext },
    useWSS: true,
  });
  fastify.register(
    await getFastifyPlugin({
      router: appRouter,
      trpcApiEndpoint: TPRC_API_ENDPOINT,
      playgroundEndpoint: TRPC_PLAYGROUND_ENDPOINT,
      request: {
        superjson: true,
      },
    }),
    { prefix: TRPC_PLAYGROUND_ENDPOINT }
  );
  fastify.listen({ port: PORT, host: HOST }, (error, address) => {
    if (error) {
      console.error(error);
      process.exit(1);
    }
    console.log();
    console.log(`\t Local: http:127.0.0.1:${PORT}/`);
    console.log(`\t Network: http:${ip.address()}:${PORT}/`);
    console.log();
  });
})();
MatsDK commented 9 months ago

You are applying the superjson transformer, right? like this:

const t = initTRPC.create({ transformer: superjson });
const router = t.router
const publicProcedure = t.procedure

I don't see why this wouldn't work, with which query does it break, and how are you calling it in the playground?

CrispenGari commented 9 months ago

You are applying the superjson transformer, right? like this:

const t = initTRPC.create({ transformer: superjson });
const router = t.router
const publicProcedure = t.procedure

I don't see why this wouldn't work, with which query does it break, and how are you calling it in the playground?

I have used trpc-playground with express when you just the key word await you get autocompletion for both your queries and mutations. Here is how I'm calling my queries but the error is saying query is not defined.

await query(...)

export {}
MatsDK commented 9 months ago

Oh I see. Since you are using trpc v10, you should call your queries and mutations with the proxy client like this:

await trpc.hello.greeting.query(...)
export {}

You should also get autocompletion on the trpc object.

If you have superjson set to true in the trpc-playground config, you should also apply this to your trpc router:

import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
export const t = initTRPC.create({
  transformer: superjson,
});
CrispenGari commented 9 months ago

Oh I see. Since you are using trpc v10, you should call your queries and mutations with the proxy client like this:

await trpc.hello.greeting.query(...)
export {}

You should also get autocompletion on the trpc object.

If you have superjson set to true in the trpc-playground config, you should also apply this to your trpc router:

import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
export const t = initTRPC.create({
  transformer: superjson,
});

Ohh i was missing that. Thanks for being patient with me and it worked now.