Trader Launchpad
Trader Launchpad15mo ago

[HOW TO?] Call trpc endpoints from vanilla nextJs api routes

Spinoff from an og thread here: https://discordapp.com/channels/867764511159091230/1032301198990135347 HI! I am wanting to do the same thing...call a trpc route from within a nextjs public api endpoint. I am using a fresh t3-turbo-with-clerk project which has trpc in packages/api (@acme/api) here is my apps/nextjs/src/pages/api/post.ts file
import { NextApiRequest, NextApiResponse } from "next";
import { appRouter } from "@acme/api";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
import { TRPCError } from "@trpc/server";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const caller = appRouter.createCaller({});

try {
const result = await caller.post.all();
res.status(200).json(result);
} catch (e) {
console.error(e);
const code = e instanceof TRPCError ? getHTTPStatusCodeFromError(e) : 500;
res.status(code).json({ error: e });
}
};

export default handler;
import { NextApiRequest, NextApiResponse } from "next";
import { appRouter } from "@acme/api";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
import { TRPCError } from "@trpc/server";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const caller = appRouter.createCaller({});

try {
const result = await caller.post.all();
res.status(200).json(result);
} catch (e) {
console.error(e);
const code = e instanceof TRPCError ? getHTTPStatusCodeFromError(e) : 500;
res.status(code).json({ error: e });
}
};

export default handler;
10 Replies
Trader Launchpad
Trader Launchpad15mo ago
i am getting a type error on
const caller = appRouter.createCaller({});
const caller = appRouter.createCaller({});
Argument of type '{}' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.
Argument of type '{}' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.
and when i load the page i get a json error:
{"error":{"code":"INTERNAL_SERVER_ERROR","name":"TRPCError"}}
{"error":{"code":"INTERNAL_SERVER_ERROR","name":"TRPCError"}}
Can someone assist me with the issue? I know I could use https://github.com/jlalmes/trpc-openapi to expose routes, I just wanted to get a basic example working first and then look into that. I am only exposing routes atm for testing purposes from what I can see, const caller = appRouter.createCaller({}); needs a ctx parameter. my @acme/api/index.ts file has
export type { AppRouter } from "./src/router";
export { appRouter } from "./src/router";

export { createContext } from "./src/context";
export type { Context } from "./src/context";
export type { AppRouter } from "./src/router";
export { appRouter } from "./src/router";

export { createContext } from "./src/context";
export type { Context } from "./src/context";
Nick
Nick15mo ago
Have you looked at our nextjs docs?
Nick
Nick15mo ago
You shouldn't need createCaller We have serverside helpers Problem looks like you're not actually creating the necessary ctx object but you shouldn't need to do it here
Trader Launchpad
Trader Launchpad15mo ago
the documentation explains using createCaller for nextjs api endpoints via that link above. Why wouldnt I need it? I have used the SSG helper in a pages/post/[id].tsx page, but didnt think that was the correct way to use it in an API route per the trpc documentation
Nick
Nick15mo ago
Got it, I'm not really a user of next, but yes looks like we do recommend that in some situations You'll need to call your createContext function {} is not enough
Trader Launchpad
Trader Launchpad15mo ago
ok, and in that function it expects some parameters (auth). However isnt the context object already set up inside @acme/api somewhere? Here is my pages/dashboard page where I load the posts into a flashlist:
import { HomeScreen as FeatureHomeScreen } from "@acme/app/features/home/screen";
import MainLayout from "@acme/app/layouts/MainLayout";
export default function HomeScreen() {
return <FeatureHomeScreen />;
}
import { HomeScreen as FeatureHomeScreen } from "@acme/app/features/home/screen";
import MainLayout from "@acme/app/layouts/MainLayout";
export default function HomeScreen() {
return <FeatureHomeScreen />;
}
Nick
Nick15mo ago
createContext is called by the adapter, but createCaller makes your calling code into the adapter That first parameter is the context values
Trader Launchpad
Trader Launchpad15mo ago
screens are located in packages/app/features (@acme/app) and here is the Homescreen linked from above file
import React from "react";

import { Link } from "solito/link";
import { Button, TouchableOpacity } from "../../design/button";
import { Text } from "../../design/typography";
import { View, SafeAreaView } from "../../design/view";

import type { AppRouter } from "@acme/api";
import type { inferProcedureOutput } from "@trpc/server";

import { trpc } from "../../utils/trpc";
import { FlashList } from "@shopify/flash-list";


export function HomeScreen() {
const { data: postData, isLoading, error } = trpc.lesson.all.useQuery();
.........
import React from "react";

import { Link } from "solito/link";
import { Button, TouchableOpacity } from "../../design/button";
import { Text } from "../../design/typography";
import { View, SafeAreaView } from "../../design/view";

import type { AppRouter } from "@acme/api";
import type { inferProcedureOutput } from "@trpc/server";

import { trpc } from "../../utils/trpc";
import { FlashList } from "@shopify/flash-list";


export function HomeScreen() {
const { data: postData, isLoading, error } = trpc.lesson.all.useQuery();
.........
trpc works fine on this page and loads the posts. Do i need to re-import clerk modules on pages/api/post to rebuild context? i would think i could import context from @acme/api, then insert it into createCaller() and yes i do believe context is the issue with my code because of the error:
Argument of type '{}' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.
Argument of type '{}' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.
but it is asking for ctx to have auth (clerk) and prisma, which id either have to import again or re-use from @acme/api here is another github I have also been looking at: https://github.com/trpc/trpc/issues/1724 this is code for the openapi generator that we should be able to pull some code from to assist. This is where I got the understanding that createCaller needs a ctx object and that ceateContext probably needs to be imported...but this is as far as I have got so atm in my pages/api/posts.ts i have (shorted for brevity)
.....
import { createContext } from "@acme/api/";

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
const { method } = req;

switch (method) {
case "GET":
try {
const ctx = await createContext({ req, res });
const caller = appRouter.createCaller({ ctx });
const data = await caller.post.all();
console.log(data);
...
.....
import { createContext } from "@acme/api/";

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
const { method } = req;

switch (method) {
case "GET":
try {
const ctx = await createContext({ req, res });
const caller = appRouter.createCaller({ ctx });
const data = await caller.post.all();
console.log(data);
...
and I am still getting error
Argument of type '{ ctx: { auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }; }' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.
Argument of type '{ ctx: { auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }; }' is not assignable to parameter of type '{ auth: SignedInAuthObject | SignedOutAuthObject; prisma: PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined>; }'.