🚀
【NextJs + BetterAuth + Polar】Subcription サブスクサービスの実装【#4 superjson】
【#4 superjson】
YouTube: https://youtu.be/VQCiwJdJ6oE
今回はtRPCのsuperjsonの設定を有効にします。
こちらを使用するとDate/Map/Setを使用したデータがある場合、
データの中身を変更することなく、
サーバーサイドとクライアントサイドで
データの不一致を防止することができます。
trpc/client.tsx
"use client";
// ^-- to make sure we can mount the Provider from a server component
import type { QueryClient } from "@tanstack/react-query";
import { QueryClientProvider } from "@tanstack/react-query";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import { createTRPCContext } from "@trpc/tanstack-react-query";
import { useState } from "react";
import { makeQueryClient } from "./query-client";
import type { AppRouter } from "./routers/_app";
import superjson from "superjson";
export const { TRPCProvider, useTRPC } = createTRPCContext<AppRouter>();
let browserQueryClient: QueryClient;
function getQueryClient() {
if (typeof window === "undefined") {
// Server: always make a new query client
return makeQueryClient();
}
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
function getUrl() {
const base = (() => {
if (typeof window !== "undefined") return "";
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
return "http://localhost:3000";
})();
return `${base}/api/trpc`;
}
export function TRPCReactProvider(
props: Readonly<{
children: React.ReactNode;
}>
) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient();
const [trpcClient] = useState(() =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
transformer: superjson,
url: getUrl(),
}),
],
})
);
return (
<QueryClientProvider client={queryClient}>
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{props.children}
</TRPCProvider>
</QueryClientProvider>
);
}
trpc/query-client.ts
import {
defaultShouldDehydrateQuery,
QueryClient,
} from "@tanstack/react-query";
import superjson from "superjson";
export function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 30 * 1000,
},
dehydrate: {
serializeData: superjson.serialize,
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === "pending",
},
hydrate: {
deserializeData: superjson.deserialize,
},
},
});
}
trpc/init.ts
import { initTRPC } from "@trpc/server";
import { cache } from "react";
import superjson from "superjson";
export const createTRPCContext = cache(async () => {
/**
* @see: https://trpc.io/docs/server/context
*/
return { userId: "user_123" };
});
// Avoid exporting the entire t-object
// since it's not very descriptive.
// For instance, the use of a t variable
// is common in i18n libraries.
const t = initTRPC.create({
/**
* @see https://trpc.io/docs/server/data-transformers
*/
transformer: superjson,
});
// Base router and procedure helpers
export const createTRPCRouter = t.router;
export const createCallerFactory = t.createCallerFactory;
export const baseProcedure = t.procedure;
Discussion