How to SSG a page from my T3 app??

I have been following this documentation: https://trpc.io/docs/nextjs/ssg So I have a [chat].tsx route and I put this in the page component:
import { createServerSideHelpers } from "@trpc/react-query/server";
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from "next";
import { appRouter } from "@/server/api/root";
import { prisma } from "@/server/db";

type ChatRouteQuery = {
chat: string;
};

export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx,
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
export const getStaticPaths: GetStaticPaths = async () => {
const chats = await prisma.chat.findMany({
select: {
id: true,
},
});
return {
paths: chats.map((chat) => ({
params: {
chat: chat.id,
},
})),
fallback: "blocking",
};
};
import { createServerSideHelpers } from "@trpc/react-query/server";
import {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from "next";
import { appRouter } from "@/server/api/root";
import { prisma } from "@/server/db";

type ChatRouteQuery = {
chat: string;
};

export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx,
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
export const getStaticPaths: GetStaticPaths = async () => {
const chats = await prisma.chat.findMany({
select: {
id: true,
},
});
return {
paths: chats.map((chat) => ({
params: {
chat: chat.id,
},
})),
fallback: "blocking",
};
};
But, ctx is giving me ts errors.
No value exists in scope for the shorthand property ctx. Either declare one or provide an initializer.
No value exists in scope for the shorthand property ctx. Either declare one or provide an initializer.
And when I do ctx: {} I get this
Type '{}' is missing the following properties from type '{ session: Session | null; prisma: PrismaClient<PrismaClientOptions, never, RejectOnNotFound | RejectPerOperation | undefined>; }': session, prismats(2739)
types.d.ts(5, 5): The expected type comes from property 'ctx' which is declared here on type 'CreateSSGHelpersOptions<CreateRouterInner<RootConfig<{ ctx: { session: Session | null; prisma: PrismaClient<PrismaClientOptions, never, RejectOnNotFound | RejectPerOperation | undefined>; }; meta: object; errorShape: { ...; }; transformer: typeof SuperJSON; }>, { ...; }>>'
Type '{}' is missing the following properties from type '{ session: Session | null; prisma: PrismaClient<PrismaClientOptions, never, RejectOnNotFound | RejectPerOperation | undefined>; }': session, prismats(2739)
types.d.ts(5, 5): The expected type comes from property 'ctx' which is declared here on type 'CreateSSGHelpersOptions<CreateRouterInner<RootConfig<{ ctx: { session: Session | null; prisma: PrismaClient<PrismaClientOptions, never, RejectOnNotFound | RejectPerOperation | undefined>; }; meta: object; errorShape: { ...; }; transformer: typeof SuperJSON; }>, { ...; }>>'
Not sure why this error is not addressed in the docs.
Static Site Generation | tRPC
Reference project//github.com/trpc/examples-next-prisma-todomvc
30 Replies
cje
cjeβ€’2y ago
your trpc handler needs context that might contain prisma, session data, etc if you used create t3 app, we have a createInnerTRPCContext function that you can export and call this will generate context without needing req and res, which you don't have in SSG
kenny
kennyOPβ€’2y ago
where do I call it?
cje
cjeβ€’2y ago
ctx: createInnerTRPCContext({ session: null }) exact syntax will differ depending on your packages etc
kenny
kennyOPβ€’2y ago
I can't find it in the t3 doc where is it imported from
cje
cjeβ€’2y ago
trpc.ts
cje
cjeβ€’2y ago
Server-Side Helpers | tRPC
createServerSideHelpers provides you with a set of helper functions that you can use to prefetch queries on the server. This is useful for SSG, but also for SSR if you opt not to use ssr: true.
cje
cjeβ€’2y ago
read through this article our docs aren't meant to teach how trpc works, thats what the trpc docs are for
kenny
kennyOPβ€’2y ago
oww there createContext is awaited
cje
cjeβ€’2y ago
if the function that generates your context returns a promise, you need to await it if not then not
kenny
kennyOPβ€’2y ago
yeh i know but why createContext instead of createTRPCContext createContext does not exist in my code. What a hassle to implement SSG in trpc so, I created this file context.ts in server folder:
import { prisma } from "@/server/db";
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type DefaultSession } from "next-auth";
interface Session extends DefaultSession {
user: {
id: string;
// ...other properties
// role: UserRole;
} & DefaultSession["user"];
}

/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {
session: Session | null;
}
/**
* Inner context. Will always be available in your procedures, in contrast to the outer context.
*
* Also useful for:
* - testing, so you don't have to mock Next.js' `req`/`res`
* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`
*
* @see https://trpc.io/docs/context#inner-and-outer-context
*/
export async function createContextInner(opts: CreateInnerContextOptions) {
return {
prisma,
session: opts.session,
};
}
import { prisma } from "@/server/db";
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type DefaultSession } from "next-auth";
interface Session extends DefaultSession {
user: {
id: string;
// ...other properties
// role: UserRole;
} & DefaultSession["user"];
}

/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {
session: Session | null;
}
/**
* Inner context. Will always be available in your procedures, in contrast to the outer context.
*
* Also useful for:
* - testing, so you don't have to mock Next.js' `req`/`res`
* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`
*
* @see https://trpc.io/docs/context#inner-and-outer-context
*/
export async function createContextInner(opts: CreateInnerContextOptions) {
return {
prisma,
session: opts.session,
};
}
Then I import :
import { createContextInner } from "@/server/context";
import { createContextInner } from "@/server/context";
then use it like this????
export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createContextInner(),
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createContextInner(),
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
That's what the docs say But it expects arguments
cje
cjeβ€’2y ago
the name of the functions is arbitrary. there's no magic going on here. it requires arguments because it's a function that has typed arguments: export async function createContextInner(opts: CreateInnerContextOptions) the type of the args is a few lines up, or you can have intellisense tell you what it needs in your case it's a nullable session. there's no session in ssg, so you make it null
kenny
kennyOPβ€’2y ago
I'll try that
arete
areteβ€’2y ago
i use the serverside helper but after generating it a few page it show and error, something like this any ideas? sorry for out of topic
kenny
kennyOPβ€’2y ago
ghmm, I dunno this is the first time I see an error like that. Try creating your own post in questions. This is still hella confusin
cje
cjeβ€’2y ago
its just typescript has nothing to do with trpc you cant call a function with incorrect arguments if you define function someFunc({ foo: number }) {} and then try to call someFunc() without args you'll get an error thats all it is
kenny
kennyOPβ€’2y ago
That's what it is yes but it's still complaining
kenny
kennyOPβ€’2y ago
here it's being done differntly
cje
cjeβ€’2y ago
its being done in the exact same way in that example look at what ssgInit does
kenny
kennyOPβ€’2y ago
yes just divided into different files my createTRPCContext was missing session
export async function createInnerTRPCContext(opts: CreateInnerContextOptions) {
const session = await getSession({ req: opts.req });
return {
prisma,
session,
...opts,
};
}
export async function createInnerTRPCContext(opts: CreateInnerContextOptions) {
const session = await getSession({ req: opts.req });
return {
prisma,
session,
...opts,
};
}
export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createInnerTRPCContext({}),
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
export async function getStaticProps(
context: GetStaticPropsContext<{ chat: string }>
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createInnerTRPCContext({}),
});
const chatId = context.params?.chat as string;
await helpers.chat.getOne.prefetch(chatId);
return {
props: {
trpcState: helpers.dehydrate(),
chatId,
},
revalidate: 1,
};
}
Honestly a pain to setup thx The whole reason for trying to implement ssg was to check if the chat is not found and return {notFound: true} Which I am struggling with atm I had this in the trpc query
getOne: publicProcedure.input(z.string()).query(async ({ ctx, input }) => {
const foundChat = await ctx.prisma.chat.findUnique({
where: { id: input },
include: { users: true },
});

if (!foundChat) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Chat not found",
});
}

return foundChat;
}),
getOne: publicProcedure.input(z.string()).query(async ({ ctx, input }) => {
const foundChat = await ctx.prisma.chat.findUnique({
where: { id: input },
include: { users: true },
});

if (!foundChat) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Chat not found",
});
}

return foundChat;
}),
const chatId = context.params?.chat as string;
const response = await helpers.chat.getOne.prefetch(chatId);
console.log("RESPONSE", response);
const chatId = context.params?.chat as string;
const response = await helpers.chat.getOne.prefetch(chatId);
console.log("RESPONSE", response);
I then try to log the respone, it returns undefined eventhough I can still see the page with the chatData ahh,
noctate
noctateβ€’2y ago
May I Ask what data do you need statically generated? Seems odd for a chat
kenny
kennyOPβ€’2y ago
I want to return a 404 page if the chat was not found
kenny
kennyOPβ€’2y ago
Static Site Generation | tRPC
Reference project//github.com/trpc/examples-next-prisma-todomvc
kenny
kennyOPβ€’2y ago
following this guide why can't I use the data that gets returned from trpc state?? the return statement of the getStaticProps
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
revalidate: 1,
};
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
revalidate: 1,
};
I'd have to destructure like crazy tho . . .
const {json:{queries[0]:{state:{data:chatData}}}} = trpcState;
const {json:{queries[0]:{state:{data:chatData}}}} = trpcState;
cje
cjeβ€’2y ago
read that page again you get the data by using the query in the component
kenny
kennyOPβ€’2y ago
ok but how would you generate 404 page?
cje
cjeβ€’2y ago
why would you generate a 404 page in ssg
kenny
kennyOPβ€’2y ago
If the page is not found if the chatId is invalid
π›ˆπž‚π—Ώπžˆπ’†π’ˆπ–π°π›ˆ 𝐜0πŸƒ3𝗿
Error pages don't need ssg, just render it normally without serversideprops or staticprops
kenny
kennyOPβ€’2y ago
yeh
Want results from more Discord servers?
Add your server