次のセットアッププロジェクト.JS , prisma , trpc , nextauth


背景


最近、私は小さなプロジェクトに取り組んでいました.そこではあまり使われていない技術を使って、それをすべて実践することができました.目的はクローンを作ることでしたKubool これは匿名メッセージを送信できるプラットフォームです.それで始めました.
最初のスタックの選択は以下の通りです.
  • 日本学術振興会
  • プリマ
  • 鉄道からのPostgres
  • タイプスクリプト
  • ネクサス
  • フォーミク+アップ
  • CSS
  • ベセル
  • 私は先にクローンの最初の基本的なバージョンを構築し、Vercelでそれを展開した.ここでチェックできます.https://anony-sender.vercel.app . その後、私は最終的に私は、エンドエンドのtypesafe APIを提供するツールであるTRPCを含むようにAPIエンドポイントを再構築させることを決めた.

    設定手順


    プロジェクトのセットアップでは、上記の技術を使用します.必要条件:
  • ノード.js
  • テキスト/コードエディタ
  • 優先コマンドラインツール( Windows端末)
  • ジット
  • ステップ1:作成&クローンレポスターターテンプレートから


    私は、上記の技術でプロジェクトを構築することに関心を集中するようになったので、私は前進し、新しいプロジェクトを開始するたびに自分のためのスターターテンプレートを作成しました.それで、そのスターターテンプレートの上に構築しましょう.
    スターターテンプレートへのリンクhttps://github.com/JohannesMogashoa/nextjs-prisma
    作成し、ローカルマシンにレポをクローニングした後.コマンドラインツールでそのプロジェクトを開くことができます.実行して依存関係をインストールしますnpm install or yarn install

    ステップ2 :プロジェクトにTRPCを追加する


    公式TRPCサイトには、以下のようにします.https://trpc.io/ ドキュメントを見てみましょう.
    この時点で、コマンドラインツールまたはVSコードの統合ターミナルを使用できます.
    yarn add @trpc/client @trpc/server @trpc/react @trpc/next zod react-query superjson ws
    
    次の手順
  • クリエイトアbackend プロジェクトのルートのフォルダ
  • クリエイトアutils and routers フォルダ内のbackend フォルダ
  • インサイドrouters フォルダを作成するindex.ts ファイル
  • インサイドutils フォルダクリエイトcontext.ts and createRouter.ts ファイル
  • インサイドlib フォルダを作成trpc.ts ファイル
  • クリエイトアtrp フォルダの内部pages/api/ , 次に、[trpc].ts ファイル.
  • 上記のファイルへのパスは次のようになります.
    - backend/routers/index.ts
    - backend/utils/context.ts
    - backend/utils/createRouter.ts
    - lib/trpc.ts
    - pages/api/trpc/[trpc].ts
    

    ステップ3 -バックエンドフォルダを設定する


    バックエンド/utils/context
    アプリはPrismaとNextauthを使用しているので、これらの“値”を通して簡単にアクセシビリティのコンテキストを使用してTrpcルートの残りの部分を通過することができます.
    import { getSession } from "next-auth/react";
    import * as trpc from '@trpc/server';
    import * as trpcNext from '@trpc/server/adapters/next';
    import { NodeHTTPCreateContextFnOptions } from '@trpc/server/adapters/node-http';
    import { IncomingMessage } from 'http';
    import ws from 'ws';
    import {prisma} from "@/lib/prisma"
    
    上記のインポート文をcontext.ts 次にファイルの下のセクションをコピーします.
    export const createContext = async ({
        req,
        res,
    }:
        | trpcNext.CreateNextContextOptions
        | NodeHTTPCreateContextFnOptions<IncomingMessage, ws>) => {
        const session = await getSession({ req });
        return {
            req,
            res,
            prisma,
            session,
        };
    };
    
    export type Context = trpc.inferAsyncReturnType<typeof createContext>;
    
    バックエンド/utils
    下のコードをcreateRouter.ts ファイル
    import { Context } from "./context";
    import * as trpc from "@trpc/server";
    
    export function createRouter() {
        return trpc.router<Context>()
    }
    
    バックエンド/ルーター/インデックス
    このファイルでは、TRPCアプリのルータを使用したいと思うすべての異なるルートで作成されます.以下の例は、すべてのルートを同じファイルにしたい場合です.
    import { createRouter } from '@/backend/utils/createRouter';
    import superjson from "superjson"
    import { z } from 'zod';
    
    export const appRouter = createRouter()
        .transformer(superjson)
        .mutation('set-username', {
            input: z.object({
                username: z.string(),
                email: z.string().email()
            }),
            async resolve({ ctx, input }) {
    
                await ctx.prisma!.user.update({
                    where: {
                        email: input.email,
                    },
                    data: {
                        slug: input.username,
                    },
                })
    
                return { success: true, message: "Username set successfully" }
            }
        })
        .query('get-user', {
            input: z.object({
                email: z.string().email()
            }),
            async resolve({ ctx, input }) {
                const user = await ctx.prisma!.user.findUnique({
                    where: {
                        email: input.email,
                    },
                })
    
                return { success: true, user }
            }
        })
    
    // export type definition of API
    export type AppRouter = typeof appRouter;
    
    あなたが想像できるように、他のルートとそれらのそれぞれのロジックを追加すると、上記のコードはますます大きくなります.それで、より良い方法は別のファイルでルートと場所をグループ化することになっていて、それらのルートを承認に輸入するでしょう.別のファイルを作成するuserRoutes.ts インサイドbackend/routers フォルダ.リファクタリングされたコードは以下のような性質を持つでしょう.
    // backend/routers/index.ts
    
    import { createRouter } from '@/backend/utils/createRouter';
    import superjson from "superjson"
    import { userRouter } from './userRouter';
    
    export const appRouter = createRouter()
        .transformer(superjson)
        .merge('user', userRouter)
    // other merged routes here
    
    // export type definition of API
    export type AppRouter = typeof appRouter;
    
    // backend/routers/userRouter.ts
    
    import { z } from "zod"
    import { createRouter } from "../utils/createRouter"
    
    export const userRouter = createRouter()
        .mutation('set-username', {
            input: z.object({
                username: z.string(),
                email: z.string().email()
            }),
            async resolve({ ctx, input }) {
    
                await ctx.prisma!.user.update({
                    where: {
                        email: input.email,
                    },
                    data: {
                        slug: input.username,
                    },
                })
    
                return { success: true, message: "Username set successfully" }
            }
        })
        .query('get-user', {
            input: z.object({
                email: z.string().email()
            }),
            async resolve({ ctx, input }) {
                const user = await ctx.prisma!.user.findUnique({
                    where: {
                        email: input.email,
                    },
                })
    
                return { success: true, user }
            }
        })
    
    すべての上記のコードを追加すると、一時的にbackend/ フォルダとは、他のフォルダ内の他のファイルに移動することができます.
    lib/trpc.TS
    import { AppRouter } from '@/backend/routers';
    import { createReactQueryHooks } from '@trpc/react';
    
    export const trpc = createReactQueryHooks<AppRouter>();
    
    ページ/API/trpc/[ trpc ].TS
    この部分は、アプリケーションルータで定義されたルータになされた要求を処理するTRPC APIエンドポイントの公式設定です.
    import { appRouter, AppRouter } from '@/backend/routers';
    import { inferProcedureOutput } from '@trpc/server';
    import * as trpcNext from '@trpc/server/adapters/next';
    import { createContext } from '@/backend/utils/context';
    
    // export API handler
    export default trpcNext.createNextApiHandler({
        router: appRouter,
        createContext,
        onError({ error }) {
            if (error.code === 'INTERNAL_SERVER_ERROR') {
                // send to bug reporting
                console.error('Something went wrong', error);
            }
        },
    });
    
    export type inferQueryResponse<
        TRouteKey extends keyof AppRouter["_def"]["queries"]
        > = inferProcedureOutput<AppRouter["_def"]["queries"][TRouteKey]>;
    
    この時点で、作成したすべてのファイルに必要なコードを追加して完了しました.現在、TRPCの最終的な実装に移動しますpages/_app.tsx ファイル.

    ステップ4 -ページを編集/アプリ。TSXファイル


    移動するpages/_app.tsx ファイルを作成し、次のように変更します.
  • 削除するexport default MyApp 次のコードをペーストします
  • // Initializing TRPC server on the Next.js server
    import { withTRPC } from "@trpc/next";
    import type { AppRouter } from "@/backend/routers";
    
    // Check to see the current environment then generate the appropriate URL
    function getBaseUrl() {
        if (process.browser) return ""; // Browser should use current path
        if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
    
        return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
    }
    
    export default withTRPC<AppRouter>({
        config({ ctx }) {
            /**
             * If you want to use SSR, you need to use the server's full URL
             * @link https://trpc.io/docs/ssr
             */
            const url = `${getBaseUrl()}/api/trpc`;
    
            return {
                url,
                /**
                 * @link https://react-query.tanstack.com/reference/QueryClient
                 */
                // queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
            };
        },
        /**
         * @link https://trpc.io/docs/ssr
         */
        ssr: false,
    })(MyApp);
    
    Phew!!!それはたくさんのコードでした.この時点で、「フロントエンド」を「バックエンド」に接続する必要がありますutils/trpc.ts ファイル.

    ステップ5 - trpcを使う


    すべてがバックエンドで実装されているので、クライアントのインスタンスを使用してどこでも使用できるようになりました.FREEの下でResponse Queryを使用しているTRPCのフックを使用して、データベースからユーザを得てみましょう.
    import { trpc } from "@/utils/trpc";
    import { useSession } from "next-auth/react";
    import React from "react";
    
    const UserComponent: React.FC = () => {
        const {data: session} = useSession()
        const {data} = trpc.useQuery(["get-user", { email: session?.user?.email as string}]);
    
        return (
            <div>
                <h1>{data?.user?.name}</h1>
                <p>{data?.user?.email}</p>
            </div>
        );
    };
    
    export default UserComponent;
    
    この時点で、あなたはあなたのNextJSアプリケーションにTRPCを実装していると幸福に言うことができます.

    アウトロ


    私を信じていないかどうか、私は書かれていたと次の組み込みのAPIルーティングとハンドリングシステムを使用してアプリケーションを完了したが、コードは混乱していた、すべての場所でのタイピングは、バックエンドとクライアント間のエンドツーエンド型の安全性のいくつかの並べ替えを模倣するために.私はTRPCは本当に便利なツールであることを発見し、より頻繁にそれを使用することを楽しみにしています.あなたがNextauthなしでtrpcを使用するgithubレポをチェックアウトしたいならば、見てくださいRoundest by Theo Browne

    便利なリンク

  • TRPC -https://trpc.io/
  • ベセルhttps://vercel.com/
  • プリズマhttps://www.prisma.io/
  • nextjs -https://nextjs.org/
  • 鉄道https://railway.app/
  • タイプスクリプト-https://www.typescriptlang.org/
  • ネクサスhttps://next-auth.js.org/
  • Tailwind CSS - https://tailwindcss.com/