reactjstypescriptexpressmiddlewaretrpc

Handling middleware in tRPC, calling multiple methods for one route?


I'm doing a group project that we only have a couple weeks to complete. We've decided to use the T-3 stack and have selected tRPC to build the server. I'm only familiar with express and have not found much help online with learning tRPC. I've got some basic methods to function such as returning a "hello user" from the backend when called on the frontend. Our application is going to be connecting to a user database and comparing inputted SQL Queries and returning metrics on those queries.

I'm having trouble building more complex routes on the backend, I can't seem to figure out how to call multiple middleware in the same router.

Here is an example of a similar application and how their express server looks when routing and calling the different middleware:

const router = Router();

// check if database being added is valid
// WIP
router.post(
  '/uri',
  connectController.connectDB,
  connectController.createExtension,
  databaseController.dbStats,
  (req, res) => {
    console.log(res.locals);
    return res.status(200).json(res.locals);
  }
);

// delivers metrics from database
// notice: the database controllers will console log, but not return errors so that individual charts can render
router.post(
  '/queryMetrics',
  connectController.connectDB,
  connectController.createExtension,
  databaseController.queryTimes,
  databaseController.numOfRows,
  databaseController.topCalls,
  databaseController.dbStats,
  databaseController.cacheHitRatio,
  databaseController.statActivity,
  (req, res) => {
    return res.status(200).json(res.locals.result);
  }
);

This is the beginning of my tRPC router, how would i call multiple methods for the same route in the way the above example uses the middleware and router?

export const exampleRouter = createTRPCRouter({
  hello: publicProcedure
    .input(z.object({ text: z.string() }))
    .query(({ input }) => {
      return {
        greeting: `Hello ${input.text}`,
      };
    }),

  getAll: publicProcedure.query(async ({ ctx }) => {
    
    // return ctx.prisma.user.findMany();

    console.log(ctx.session)
    const users = await ctx.prisma.user.findMany();
    const names = users.map(user => user.name);
    return names;
    
  }),

  getSecretMessage: protectedProcedure.query(() => {
    return "you can now see this secret message!";
  }),

  connectToUserDb: protectedProcedure
    .input(z.object({ URI: z.string() }))
    .query( ({input, ctx}) => {
      const pool = new Pool({
        connectionString: input.URI
      });

      pool.query('SELECT NOW()', (error, result) => {
        if (error) {
          console.error('Error executing query:', error);
          return;
        }
      
        console.log('Connection to "postgres" database successful');
        console.log('Current timestamp:', result.rows[0].now);
      });

      ctx.dbConnection = pool;
})

How would I design a route that called each of these methods on this example router? Do you think we should continue with tRPC or are we better off switching to express to finish the project?


Solution

  • You will need to extract the logic from each of the different procedures that you want. This is what has been recommended from one of the maintainers of trpc, as it is not recommended to call trpc procedures from other procedures.

    Here's a video and around the linked timestamp he starts actually using this method of separation below. Might not seem the best to separate it out like this since you are early in your project but once you have multiple routers, and you need to be able to get information from other routers you will start to see the benefit.

    Also I would recommend joining the trpc discord and theo's discord and searching in both of them, has helped me a ton.

    const helloLogic = () => {
         greeting: `Hello ${input.text}`,
    };
    
    const getAllLogic = async ({ prisma }) => {
         console.log(ctx.session)
         const users = await ctx.prisma.user.findMany();
         const names = users.map(user => user.name);
         return names;
    };
    
    export const exampleRouter = createTRPCRouter({
      hello: publicProcedure
       .input(z.object({ text: z.string() }))
       .query(({ input }) => {
        return helloLogic();
      }),
      getAll: publicProcedure.query(async ({ ctx }) => {
        return await getAllLogic()),
      }),
    
      // ** Everything you need procedure **
      callAllMethods: publicProcedure.query(async ({ ctx }) => {
        return {
         hello: helloLogic(),
         users: await getAllLogic()
         // Whatever else you need to call
        }
      })
    })