trpc demo

This commit is contained in:
2025-04-12 01:49:57 +02:00
parent 41059d9396
commit 10d1c91dea
15 changed files with 255 additions and 30 deletions

3
src/app/_trpc/Client.ts Normal file
View File

@@ -0,0 +1,3 @@
import { createTRPCReact } from "@trpc/react-query";
import type { TrpcRouter } from "~/server/routers/_app";
export const trpc = createTRPCReact<TrpcRouter>({})

View File

@@ -0,0 +1,22 @@
import { httpBatchLink } from "@trpc/client";
import { trpcRouter } from "~/server/routers/_app";
function getBaseUrl() {
if (typeof window !== 'undefined')
// browser should use relative path
return '';
if (process.env.VERCEL_URL)
// reference for vercel.com
return `https://${process.env.VERCEL_URL}`;
if (process.env.RENDER_INTERNAL_HOSTNAME)
// reference for render.com
return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
// assume localhost
return `http://localhost:${process.env.PORT ?? 3000}`;
};
export const servTrpc = trpcRouter.createCaller({
links: [
httpBatchLink({url: `${getBaseUrl()}/api/trpc`}),
],
});

View File

@@ -0,0 +1,38 @@
'use client'
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import React, { useState } from "react"
import { trpc } from "./Client";
function getBaseUrl() {
if (typeof window !== 'undefined')
// browser should use relative path
return '';
if (process.env.VERCEL_URL)
// reference for vercel.com
return `https://${process.env.VERCEL_URL}`;
if (process.env.RENDER_INTERNAL_HOSTNAME)
// reference for render.com
return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
// assume localhost
return `http://localhost:${process.env.PORT ?? 3000}`;
}
export default function TrpcProvider({children}:{children: React.ReactNode}) {
const [queryClient] = useState(() => new QueryClient({}));
const [trpcClient] = useState(() => {
return trpc.createClient({
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
})
});
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}> {children} </QueryClientProvider>
</trpc.Provider>
)
}

View File

@@ -0,0 +1,10 @@
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
import { trpcRouter } from '~/server/routers/_app'
const handler = (req: Request) => fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: trpcRouter,
createContext: () => ({}),
});
export {handler as GET, handler as POST}

View File

@@ -0,0 +1,26 @@
'use client'
import { trpc } from "~/app/_trpc/Client"
import CvEntry, { type CvEntryProps } from "./CvEntry"
import type { servTrpc } from "~/app/_trpc/ServerClient"
type CvCategoryProps = {
initialData: Awaited<ReturnType<typeof servTrpc.cvCategory.get>>,
children?: React.ReactElement<CvEntryProps>
}
export default function CvCategory(props:CvCategoryProps) {
const cvCategories = trpc.cvCategory.get.useQuery(undefined,{
initialData: props.initialData,
refetchOnMount: false,
refetchOnReconnect: false,
});
return (
<div>
{cvCategories.data.map((cat) => {
return (
<div key={cat.id}>
{cat.name}
</div>
)
})}
</div>
)
}

View File

@@ -0,0 +1,7 @@
import type { cvEntry } from "~/server/db/schema"
export type CvEntryProps = typeof cvEntry
export default function CvEntry(cvEntry: CvEntryProps) {
return (<></>)
}

View File

@@ -0,0 +1,3 @@
export default function CvLayout({children,}: Readonly<{ children: React.ReactNode }>) {
return (<></>)
}

View File

@@ -1,18 +1,10 @@
import { getCvCategories } from "~/server/db/query"
import { servTrpc } from "~/app/_trpc/ServerClient"
import CvCategory from "./_components/CvCategory";
export default async function CvPage() {
const cvCategories = await getCvCategories();
const cvCategories = await servTrpc.cvCategory.get();
return (
<div>
{cvCategories.map((category) => {
return (
<div key={category.id}>
<div>
{category.name}
</div>
</div>
)
})}
</div>
<CvCategory initialData={cvCategories}>
<></>
</CvCategory>
)
}

View File

@@ -5,6 +5,7 @@ import { ClerkProvider } from "@clerk/nextjs";
import { config } from "@fortawesome/fontawesome-svg-core"
import "@fortawesome/fontawesome-svg-core/styles.css"
import TopNav from "./_components/TopNav";
import TrpcProvider from "./_trpc/TrpcProvider";
config.autoAddCss = false;
export const metadata: Metadata = {
title: "Gregor Lohaus",
@@ -24,13 +25,15 @@ export default function RootLayout({
}: Readonly<{ children: React.ReactNode, modal: React.ReactNode }>) {
return (
<ClerkProvider>
<html lang="en" className={`${geist.variable}`}>
<body className="flex flex-col gap-2 bg-black text-white">
<TopNav/>
{children}
{modal}
</body>
</html>
<TrpcProvider>
<html lang="en" className={`${geist.variable}`}>
<body className="flex flex-col gap-2 bg-black text-white">
<TopNav/>
{children}
{modal}
</body>
</html>
</TrpcProvider>
</ClerkProvider>
);
}

View File

@@ -1,9 +0,0 @@
import "server-only"
import { db } from "."
export async function getCvCategories() {
const categories = await db.query.cvCategory.findMany({
orderBy: (model, {desc} ) => desc(model.name)
})
return categories;
}

View File

@@ -0,0 +1,10 @@
import { router } from "../trpc";
import { CvCategoryRouter } from "./cvCategory";
import { publicProcedure } from "~/server/trpc";
export const trpcRouter = router({
hello: publicProcedure.query(async () => "world"),
cvCategory: CvCategoryRouter
})
export type TrpcRouter = typeof trpcRouter

View File

@@ -0,0 +1,12 @@
import { db } from "~/server/db";
import { publicProcedure, router } from "~/server/trpc";
export const CvCategoryRouter = router({
get: publicProcedure.query(async () => {
const categories = await db.query.cvCategory.findMany({
orderBy: (model, {desc} ) => desc(model.name)
});
return categories;
})
})

5
src/server/trpc.ts Normal file
View File

@@ -0,0 +1,5 @@
import { initTRPC } from "@trpc/server"
const t = initTRPC.create();
export const router = t.router;
export const publicProcedure = t.procedure;