この
今回の
本当で
ちょっとした
どの データベースを 使うべきか
まず
自前で
そこで
がっくりした。
さて、
そうなると
早速、
どうしても
どの ORM を 使うか
TypeScript の
Prisma の
また、
例えば、
const findById = async ({ id }: { id: number }) => await db.select().from(countries).leftJoin(cities, eq(cities.countryId, countries.id)).where(eq(countries.id, id));
const country = findById({ id: 10 });↓
SELECT *FROM countriesLEFT JOIN cities ON cities.countryId = countries.idWHERE countries.id = 10;ほぼ SQL を
SQL は
tRPC を 使った API リクエスト
データ取得・更新を
今
例えば、
export const postRouter = { bySlug: publicProcedure.input(z.object({ slug: z.string() })).query(({ ctx, input }) => { return ctx.db.query.posts.findFirst({ where: eq(schema.posts.slug, input.slug), }); }),
incrementViews: publicProcedure.input(z.object({ slug: z.string() })).mutation(({ ctx, input }) => { return ctx.db .insert(schema.posts) .values({ slug: input.slug, views: 1 }) .onConflictDoUpdate({ target: [schema.posts.slug], set: { views: increment(schema.posts.views), updatedAt: new Date() }, }); }),} satisfies TRPCRouterRecord;それぞれが
type PostRouter = { bySlug: QueryProcedure<{ input: { slug: string }; output: { slug: string; views: number; createdAt: Date; updatedAt: Date } | undefined; }>; incrementViews: MutationProcedure<{ input: { slug: string }; output: NeonHttpQueryResult<never> }>;};Auth.js を 使って 保護された プロシージャを 定義する
tRPC では
色々試してみたかったので@auth/drizzle-adapter を
ただ、
さて、
export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: 'UNAUTHORIZED' }); } return next({ ctx: { session: { ...ctx.session, user: ctx.session.user }, }, });});上記の
使い方はpublicProcedure はprotectedProcedure
export const authRouter = createTRPCRouter({ getSession: publicProcedure.query(({ ctx }) => { return ctx.session; }),
getSecretMessage: protectedProcedure.query(() => { return 'you can see this secret message!'; }),});コンポーネント側の 処理
API エンドポイントの
Next.js は
当初は
- 戻る
ボタン等の リロードが 発生しない ページ遷移では PV が インクリメントしない - 別タブから
戻ってきた 時に 最新の PV に 更新されない
Client Component に
以下が
export const ViewCounter = ({ slug }: { slug: string }) => { const utils = api.useUtils();
const { mutate } = api.post.incrementViews.useMutation({ onSuccess: async () => await utils.post.bySlug.invalidate({ slug }), });
const { data } = api.post.bySlug.useQuery({ slug }); const views = data?.views;
React.useEffect(() => mutate({ slug }), [mutate, slug]);
if (!views) return <ViewCounterSkeleton />;
return <p className='font-sans text-sm text-muted-foreground'>{views.toLocaleString()} views</p>;};
export const ViewCounterSkeleton = () => <Skeleton className='h-4 w-14' />;tRPC にはuseEffect からmutate 関数が
こういった
データが
もう 一つの 選択肢
PV の
Upstashの今回は → 実装した)。
ただし、
Redis の
ただし、
後述する tRPC ではまた 違った 書き方と なる。 ↩

