-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
db95375
commit 53d74aa
Showing
12 changed files
with
337 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { env } from '@/env'; | ||
import { appRouter } from '@/server/api/root'; | ||
import { createTRPCContext } from '@/server/api/trpc'; | ||
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'; | ||
import type { NextRequest } from 'next/server'; | ||
|
||
/** | ||
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when | ||
* handling a HTTP request (e.g. when you make requests from Client Components). | ||
*/ | ||
const createContext = async (request: NextRequest) => { | ||
return createTRPCContext({ | ||
headers: request.headers, | ||
}); | ||
}; | ||
|
||
const handler = (request: NextRequest) => | ||
fetchRequestHandler({ | ||
endpoint: '/api/data', | ||
req: request, | ||
router: appRouter, | ||
createContext: () => createContext(request), | ||
onError: | ||
env.NODE_ENV === 'development' | ||
? ({ path, error }) => { | ||
console.error( | ||
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`, | ||
); | ||
} | ||
: undefined, | ||
}); | ||
|
||
export { handler as GET, handler as POST }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
'use client'; | ||
|
||
import { env } from '@/env'; | ||
import { api } from '@/lib/api/client'; | ||
import { createQueryClient } from '@/lib/api/queryClient'; | ||
import { type QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import { loggerLink, unstable_httpBatchStreamLink } from '@trpc/client'; | ||
import { useState } from 'react'; | ||
import SuperJSON from 'superjson'; | ||
|
||
let clientQueryClientSingleton: QueryClient | undefined = undefined; | ||
const getQueryClient = () => { | ||
if (typeof window === 'undefined') { | ||
// Server: always make a new query client | ||
return createQueryClient(); | ||
} | ||
// Browser: use singleton pattern to keep the same query client | ||
if (!clientQueryClientSingleton) { | ||
clientQueryClientSingleton = createQueryClient(); | ||
} | ||
return clientQueryClientSingleton; | ||
}; | ||
|
||
function TRPCProvider(props: { children: React.ReactNode }) { | ||
const queryClient = getQueryClient(); | ||
|
||
const [trpcClient] = useState(() => | ||
api.createClient({ | ||
links: [ | ||
loggerLink({ | ||
enabled: (op) => | ||
process.env.NODE_ENV === 'development' || | ||
(op.direction === 'down' && op.result instanceof Error), | ||
}), | ||
unstable_httpBatchStreamLink({ | ||
transformer: SuperJSON, | ||
url: `${env.SITE_URL}/api/data`, | ||
headers: () => { | ||
const headers = new Headers(); | ||
headers.set('x-trpc-source', 'nextjs-react'); | ||
return headers; | ||
}, | ||
}), | ||
], | ||
}), | ||
); | ||
|
||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
<api.Provider client={trpcClient} queryClient={queryClient}> | ||
{props.children} | ||
</api.Provider> | ||
</QueryClientProvider> | ||
); | ||
} | ||
|
||
export { TRPCProvider }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,18 @@ | ||
'use client'; | ||
|
||
import { ThemeProvider } from 'next-themes'; | ||
import { ThemeProvider as NextThemeProvider } from 'next-themes'; | ||
|
||
function NextThemeProvider({ children }: { children: React.ReactNode }) { | ||
function ThemeProvider({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<ThemeProvider | ||
<NextThemeProvider | ||
attribute='class' | ||
defaultTheme='system' | ||
enableSystem | ||
disableTransitionOnChange | ||
> | ||
{children} | ||
</ThemeProvider> | ||
</NextThemeProvider> | ||
); | ||
} | ||
|
||
export { NextThemeProvider as ThemeProvider }; | ||
export { ThemeProvider }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
'use client'; | ||
|
||
import type { AppRouter } from '@/server/api/root'; | ||
import { createTRPCReact } from '@trpc/react-query'; | ||
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'; | ||
|
||
const api = createTRPCReact<AppRouter>(); | ||
|
||
/** | ||
* Inference helper for inputs. | ||
* | ||
* @example type HelloInput = RouterInputs['example']['hello'] | ||
*/ | ||
type RouterInputs = inferRouterInputs<AppRouter>; | ||
|
||
/** | ||
* Inference helper for outputs. | ||
* | ||
* @example type HelloOutput = RouterOutputs['example']['hello'] | ||
*/ | ||
type RouterOutputs = inferRouterOutputs<AppRouter>; | ||
|
||
export { api, type RouterInputs, type RouterOutputs }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { | ||
QueryClient, | ||
defaultShouldDehydrateQuery, | ||
} from '@tanstack/react-query'; | ||
import SuperJSON from 'superjson'; | ||
|
||
function createQueryClient() { | ||
return new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
// With SSR, we usually want to set some default staleTime | ||
// above 0 to avoid refetching immediately on the client | ||
staleTime: 30 * 1000, | ||
}, | ||
dehydrate: { | ||
serializeData: SuperJSON.serialize, | ||
shouldDehydrateQuery: (query) => | ||
defaultShouldDehydrateQuery(query) || | ||
query.state.status === 'pending', | ||
}, | ||
hydrate: { | ||
deserializeData: SuperJSON.deserialize, | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
export { createQueryClient }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import 'server-only'; | ||
|
||
import { createQueryClient } from '@/lib/api/queryClient'; | ||
import { type AppRouter, createCaller } from '@/server/api/root'; | ||
import { createTRPCContext } from '@/server/api/trpc'; | ||
import { createHydrationHelpers } from '@trpc/react-query/rsc'; | ||
import { headers } from 'next/headers'; | ||
import { cache } from 'react'; | ||
|
||
/** | ||
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when | ||
* handling a tRPC call from a React Server Component. | ||
*/ | ||
const createContext = cache(() => { | ||
const heads = new Headers(headers()); | ||
heads.set('x-trpc-source', 'rsc'); | ||
|
||
return createTRPCContext({ | ||
headers: heads, | ||
}); | ||
}); | ||
|
||
const getQueryClient = cache(createQueryClient); | ||
const caller = createCaller(createContext); | ||
|
||
const { trpc: api, HydrateClient } = createHydrationHelpers<AppRouter>( | ||
caller, | ||
getQueryClient, | ||
); | ||
|
||
export { api, HydrateClient }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { testRouter } from '@/server/api/routers/test'; | ||
import { createCallerFactory, createTRPCRouter } from '@/server/api/trpc'; | ||
|
||
/** | ||
* This is the primary router for your server. | ||
* | ||
* All routers added in /api/routers should be manually added here. | ||
*/ | ||
const appRouter = createTRPCRouter({ | ||
test: testRouter, | ||
}); | ||
|
||
// export type definition of API | ||
type AppRouter = typeof appRouter; | ||
|
||
/** | ||
* Create a server-side caller for the tRPC API. | ||
* @example | ||
* const trpc = createCaller(createContext); | ||
* const res = await trpc.post.all(); | ||
* ^? Post[] | ||
*/ | ||
const createCaller = createCallerFactory(appRouter); | ||
|
||
export { appRouter, createCaller, type AppRouter }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { createTRPCRouter, publicProcedure } from '@/server/api/trpc'; | ||
import { posts } from '@/server/db/schema'; | ||
|
||
const testRouter = createTRPCRouter({ | ||
getLatest: publicProcedure.query(async ({ ctx }) => { | ||
const post = await ctx.db.query.posts.findFirst({ | ||
orderBy: (posts, { desc }) => [desc(posts.createdAt)], | ||
}); | ||
|
||
return post ?? null; | ||
}), | ||
}); | ||
|
||
export { testRouter }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { auth } from '@/server/auth'; | ||
import { db } from '@/server/db'; | ||
import { s3 } from '@/server/s3'; | ||
/** | ||
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: | ||
* 1. You want to modify request context (see Part 1). | ||
* 2. You want to create a new middleware or type of procedure (see Part 3). | ||
* | ||
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will | ||
* need to use are documented accordingly near the end. | ||
*/ | ||
import { initTRPC } from '@trpc/server'; | ||
import superjson from 'superjson'; | ||
import { ZodError } from 'zod'; | ||
|
||
/** | ||
* 1. CONTEXT | ||
* | ||
* This section defines the "contexts" that are available in the backend API. | ||
* | ||
* These allow you to access things when processing a request, like the database, the session, etc. | ||
* | ||
* This helper generates the "internals" for a tRPC context. The API handler and RSC clients each | ||
* wrap this and provides the required context. | ||
* | ||
* @see https://trpc.io/docs/server/context | ||
*/ | ||
export const createTRPCContext = async (opts: { headers: Headers }) => { | ||
return { | ||
auth, | ||
db, | ||
s3, | ||
...opts, | ||
}; | ||
}; | ||
|
||
/** | ||
* 2. INITIALIZATION | ||
* | ||
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse | ||
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation | ||
* errors on the backend. | ||
*/ | ||
const t = initTRPC.context<typeof createTRPCContext>().create({ | ||
transformer: superjson, | ||
errorFormatter({ shape, error }) { | ||
return { | ||
...shape, | ||
data: { | ||
...shape.data, | ||
zodError: | ||
error.cause instanceof ZodError ? error.cause.flatten() : null, | ||
}, | ||
}; | ||
}, | ||
}); | ||
|
||
/** | ||
* Create a server-side caller. | ||
* | ||
* @see https://trpc.io/docs/server/server-side-calls | ||
*/ | ||
const createCallerFactory = t.createCallerFactory; | ||
|
||
/** | ||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) | ||
* | ||
* These are the pieces you use to build your tRPC API. You should import these a lot in the | ||
* "/src/server/api/routers" directory. | ||
*/ | ||
|
||
/** | ||
* This is how you create new routers and sub-routers in your tRPC API. | ||
* | ||
* @see https://trpc.io/docs/router | ||
*/ | ||
const createTRPCRouter = t.router; | ||
|
||
/** | ||
* Middleware for timing procedure execution and adding an artificial delay in development. | ||
* | ||
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating | ||
* network latency that would occur in production but not in local development. | ||
*/ | ||
const timingMiddleware = t.middleware(async ({ next, path }) => { | ||
const start = Date.now(); | ||
|
||
if (t._config.isDev) { | ||
// artificial delay in dev | ||
const waitMs = Math.floor(Math.random() * 400) + 100; | ||
await new Promise((resolve) => setTimeout(resolve, waitMs)); | ||
} | ||
|
||
const result = await next(); | ||
|
||
const end = Date.now(); | ||
console.log(`[TRPC] ${path} took ${end - start}ms to execute`); | ||
|
||
return result; | ||
}); | ||
|
||
/** | ||
* Public (unauthenticated) procedure | ||
* | ||
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not | ||
* guarantee that a user querying is authorized, but you can still access user session data if they | ||
* are logged in. | ||
*/ | ||
const publicProcedure = t.procedure.use(timingMiddleware); | ||
|
||
export { createTRPCRouter, createCallerFactory, publicProcedure }; |