🌞

T3 Stack でアプリを構築して実行してみた

2022/09/29に公開

はじめに

こんにちは、クラウドエースの伊藝です。

最近、T3 Stack というワードが話題になっています。

以下で紹介されているように、簡潔さ、モジュール性、フルスタックの型安全を重視した技術スタックです。

https://zenn.dev/mikinovation/articles/20220911-t3-stack

T3 Stack は以下のパッケージで構成されています。

今回は T3 Stack アプリを実際に構築して、実行してみます。

環境

  • Ubuntu 22.04.1 LTS (WSL)
  • yarn 1.22.15
  • node v16.16.0

アプリの雛形を生成する

以下のコマンドで T3 Stack アプリの雛形を生成します。

npm

npx create-t3-app@latest

yarn

yarn create t3-app

pnpm

pnpm dlx create-t3-app@latest

今回は yarn を使いました。

アプリの雛形を生成するコマンドを実行すると、いくつかの質問が表示されます。

括弧 (my-t3-app) の中はデフォルト値となっており、何も入力せずに Enter を押すとその値が使われます。

$ yarn create t3-app
yarn create v1.22.15
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@2.3.2: The platform "linux" is incompatible with this module.
info "fsevents@2.3.2" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Installed "create-t3-app@5.11.0" with binaries:
      - create-t3-app

   ___ ___ ___   __ _____ ___   _____ ____    __   ___ ___
  / __| _ \ __| /  \_   _| __| |_   _|__ /   /  \ | _ \ _ \
 | (__|   / _| / /\ \| | | _|    | |  |_ \  / /\ \|  _/  _/
  \___|_|_\___|_/‾‾\_\_| |___|   |_| |___/ /_/‾‾\_\_| |_|

? What will your project be called? (my-t3-app)

TypeScript と JavaScript のどちらを使うかを問われます。上下の矢印キーで選択することができます。

今回は TypeScript を選択しました。

? Will you be using JavaScript or TypeScript? (Use arrow keys)
❯ TypeScript
  JavaScript

アプリにインストールするパッケージを問われます。矢印キーで選択して、スペースキーで有効化/無効化を切り替えることができます。丸の中に黒丸が表示されたら有効化となります。

今回は欲張って全て有効化しました。

? Which packages would you like to enable? (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◉ nextAuth
 ◉ prisma
 ◯ tailwind
 ◯ trpc

アプリの雛形のディレクトリを git リポジトリとして初期化するかを問われます。(git init)

今回は初期化をします。

? Initialize a new git repository? (Y/n)

yarn install を実行して依存関係を解決するかを問われます。

どちらでもいいですが、今回は実行することを選びました。

? Would you like us to run yarn install? (Y/n)

質問が終わり、アプリの雛形の生成が行われます。

Using: yarn

✔ my-t3-app scaffolded successfully!

Installing packages...
✔ Successfully installed nextAuth
✔ Successfully installed prisma
✔ Successfully installed tailwind
✔ Successfully installed trpc
✔ Successfully installed envVariables

Initializing Git...
⠋ Creating a new git repo...
✔ Successfully initialized git

Next steps:
  cd my-t3-app
  yarn prisma db push
  yarn dev
Done in 515.51s.

アプリの雛形を眺める

アプリの雛形の生成が終わった時点では cd が行われていないため、手動でディレクトリを移動します。

cd my-t3-app

ディレクトリ内のファイルを見ると、T3 Stack を構成するライブラリに必要なファイルが生成されていることがわかります。

$ tree -a
.
├── .env
├── .eslintrc.json
├── .git
~~~~
~~~~
├── .gitignore
├── README.md
├── next-env.d.ts
├── next.config.mjs
├── node_modules
~~~~
~~~~
├── package.json
├── postcss.config.cjs
├── prisma
│   └── schema.prisma
├── public
│   └── favicon.ico
├── src
│   ├── env
│   │   ├── client.mjs
│   │   ├── schema.mjs
│   │   └── server.mjs
│   ├── pages
│   │   ├── _app.tsx
│   │   ├── api
│   │   │   ├── auth
│   │   │   │   └── [...nextauth].ts
│   │   │   ├── examples.ts
│   │   │   ├── restricted.ts
│   │   │   └── trpc
│   │   │       └── [trpc].ts
│   │   └── index.tsx
│   ├── server
│   │   ├── common
│   │   │   └── get-server-auth-session.ts
│   │   ├── db
│   │   │   └── client.ts
│   │   └── router
│   │       ├── context.ts
│   │       ├── example.ts
│   │       ├── index.ts
│   │       └── protected-example-router.ts
│   ├── styles
│   │   └── globals.css
│   ├── types
│   │   └── next-auth.d.ts
│   └── utils
│       └── trpc.ts
├── tailwind.config.cjs
├── tree.txt
├── treea.txt
├── tsconfig.json
└── yarn.lock

1838 directories, 16284 files

.env も生成されており、ライブラリに必要な環境変数をすぐに設定することができるようになっています。

生成段階で NextAuth.js の Discord の環境変数が用意されているのは Discord 推しだからなんですかねw

T3 Stack の Discord サーバーがあるので、興味がある人は参加するといいかもしれないです。

https://t3.gg/discord

# Note that not all variables here might be in use for your selected configuration
# When adding additional env variables, the schema in /env/schema.mjs should be updated accordingly

# Prisma
DATABASE_URL=file:./db.sqlite

# Next Auth
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000

# Next Auth Discord Provider
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=

Prisma の設定ファイルの schema.prisma を見ると、NextAuth.js の連携の設定がされています。

こういう設定は面倒なので、生成段階で行われるのは非常に助かります。

生成段階では Git のコミットが行われていないため、手動で行います。

$ git add . && git commit -m 'Create T3 App'

実行する

NextAuth.js に必要なシークレットキーを生成します。

openssl rand -base64 32
qi0hKVuMPBrgxJezil8L9wdpl3ZYjqCVYKizzURQCdU=

出力されたランダムな文字列を .envNEXTAUTH_SECRET= の右辺に入力します。

# Next Auth
NEXTAUTH_SECRET=qi0hKVuMPBrgxJezil8L9wdpl3ZYjqCVYKizzURQCdU=

以下コマンドでアプリを起動します。

$ yarn dev

http://localhost:3000/ にアクセスすると、初期画面が表示されます。

T3 Stack を構成するパッケージのドキュメントが紹介されています。

初期画面

画面下に表示されている Hello from tRPC は tRPC の通信のレスポンスです。

tRPC の通信はデベロッパーツールのコンソールから確認することができます。

tRPC のログ

終了するために Ctrl + C を押す。

認証機能を有効にする

NextAuth.js の認証機能を有効にするために、認証プロバイダを選ぶ必要があります。

NextAuth.js は多くの認証プロバイダをサポートしており、それぞれの設定方法が丁寧に公式ドキュメントで紹介されています。

https://next-auth.js.org/providers/

Google OAuth の設定

今回は Google OAuth を認証プロバイダとして利用するが、好きな認証プロバイダがある場合はそちらを利用して問題ないです。

Google Cloud で Google OAuth を利用するための設定を行います。

OAuth 同意画面

以下の URL から「OAuth 同意画面」の設定を行います。

https://console.cloud.google.com/apis/credentials/consent

「OAuth 同意画面」の User Type で外部を選択し、作成ボタンを押します。

OAuth 同意画面 設定 0

アプリ名と自分のメールアドレスを画像のように入力します。

OAuth 同意画面 設定 1

「スコープを追加または削除」ボタンを押して、「範囲」が openid の列にチェックを付けて選択します。

OAuth 同意画面 設定 2

テストユーザーの設定は何も入力しません。

OAuth 同意画面 設定 3

概要を確認して「OAuth 同意画面」の設定を完了します。

認証情報

以下の URL から認証情報「OAuth クライアント ID」を作成します。

https://console.cloud.google.com/apis/credentials

OAuth 認証情報 設定 0

「アプリケーションの種類」を ウェブアプリケーション に設定します。

「名前」にアプリの名前を入力します。

「承認済みの JavaScript 生成元」に http://localhost:3000 を入力します。

「承認済みのリダイレクト URI」に http://localhost:3000/api/auth/callback/google を入力します。

OAuth 認証情報 設定 1

OAuth クライアントの ID とクライアントシークレットが表示されます。

値をコピーして、.env に入力します。

今回は Discord ではなく Google OAuth を認証プロバイダとして利用するため、環境変数のキーを GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET に変更します。

OAuth 認証情報 設定 1

...
# Next Auth Discord Provider
GOOGLE_CLIENT_ID=************-********************************.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-****************************

schema.mjs

src/env/schema.mjsZod により環境変数の型が定義されています。

.env で環境変数のキーを変更したため、それに合わせてキーの名前を変更します。

変更前

export const serverSchema = z.object({
...
  DISCORD_CLIENT_ID: z.string(),
  DISCORD_CLIENT_SECRET: z.string(),
});

変更後

export const serverSchema = z.object({
...
  GOOGLE_CLIENT_ID: z.string(),
  GOOGLE_CLIENT_SECRET: z.string(),
});

[...nextauth].ts

src/pages/api/auth/[...nextauth].ts の一部を編集して、Google OAuth を認証プロバイダとして利用する設定をします。

変更前

import DiscordProvider from "next-auth/providers/discord";
...
  providers: [
    DiscordProvider({
      clientId: env.DISCORD_CLIENT_ID,
      clientSecret: env.DISCORD_CLIENT_SECRET,
    }),
    // ...add more providers here
  ],
...

変更後

import GoogleProvider from "next-auth/providers/google";
...
  providers: [
    GoogleProvider({
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
    }),
    // ...add more providers here
  ],
...

index.ts

src/pages/index.tsx を以下のように変更し、追記して認証ボタンを作成します。

ボタンは Tailwind でスタイルを適用します。

  • 関数を使うための import を追加します
  • 認証情報を使うための useSession() を追加します
  • trpc.useQuery で投げるパラメータを変更します
  • main タグ内の一番下に session があるかないかで切り替わる認証のためのボタンを追加します
import { signIn, signOut, useSession } from "next-auth/react";
...
const Home: NextPage = () => {
  const { data: session } = useSession();
  const hello = trpc.useQuery(["example.hello", { text: `from tRPC by ${session?.user?.email}` }]);
...
        <div>
          {session ? (
            <button
              onClick={() => signOut()}
              className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
            >
              Sign out
            </button>
          ) : (
            <button
              onClick={() => signIn()}
              className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
            >
              Sign in
            </button>
          )}
        </div>
      </main>
...

Prisma のマイグレーション

認証情報をデータベース (sqlite) に格納するために、マイグレーションを行います。

$ yarn prisma migrate dev --name init

実行する

認証の準備ができたのでアプリを起動します。

$ yarn dev

Hello from tRPC by undefined と表示されている下の Sign in をクリックします。

認証機能 0

Sign in with Google をクリックして Google アカウントでログインします。

認証機能 1

トップ画面に戻り、自分のメールアドレスが表示されます。

認証機能 2

おわりに

T3 Stack を構成するパッケージらはどれも設定が必要で、組み合わせて使うとなるとより多くの設定が必要となります。

しかし、create-t3-app コマンドを使うことで、簡単にそれらを含んだフルスタック Web アプリを構築することができました。

また、NextAuth.js と Prisma の連携が雛形生成時点でされていたり、Zod で環境変数の型安全を実現したり、tRPC で superjson を使う設定になっていたりと、細かい部分でも型安全とパッケージ同士の連携に気を使っていることがわかりました。

T3 Stack のパッケージを参考にして自分でアプリを構築するより、create-t3-app を使ってアプリを構築するほうが快適に開発を進めることができると考えられます。

Discussion