LoginSignup
16
25

More than 1 year has passed since last update.

無償版PaaSだけでGithubでログインするNext.jsアプリを作る

Last updated at Posted at 2021-06-06

最近next.jsでいろいろアプリを作っているのですが、そのときにnextauthというライブラリで認証機能をさくっと作れることを知りまして、作ってみようと思いました。

自分のGithubのレポジトリはこちらです。

やりたいこと

  • Next.jsでウェブアプリを作る
  • Githubで認証できるようにする
  • データベースにログイン情報を保存できるようにする
  • すべて無料サービスで行う

使う技術構成

  • Next.js
  • Typescript
  • nextauth
  • mysql
  • Docker
  • Prisma

使うサービス

  • Github App
  • Vercel
  • Heroku ClearDB

ファイル構成

tree -I node_modules
.
├── README.md
├── app
│   ├── LICENSE
│   ├── README.md
│   ├── components
│   │   ├── access-denied.tsx
│   │   ├── footer.module.css
│   │   ├── footer.tsx
│   │   ├── header.module.css
│   │   ├── header.tsx
│   │   └── layout.tsx
│   ├── global.d.ts
│   ├── next-env.d.ts
│   ├── package.json
│   ├── pages
│   │   ├── _app.tsx
│   │   ├── api
│   │   │   ├── auth
│   │   │   │   └── [...nextauth].ts
│   │   │   └── examples
│   │   │       ├── jwt.ts
│   │   │       ├── protected.ts
│   │   │       └── session.ts
│   │   ├── api-example.tsx
│   │   ├── client.tsx
│   │   ├── index.tsx
│   │   ├── policy.tsx
│   │   ├── protected.tsx
│   │   ├── server.tsx
│   │   └── styles.css
│   ├── prisma
│   │   ├── migrations
│   │   │   ├── 20210521153533_20210521
│   │   │   │   └── migration.sql
│   │   │   └── migration_lock.toml
│   │   └── schema.prisma
│   ├── tsconfig.json
│   ├── types
│   │   ├── environment.d.ts
│   │   ├── next-auth.d.ts
│   │   └── next.d.ts
│   └── yarn.lock
├── docker-compose.yml
└── mysql
    ├── Dockerfile
    └── my.cnf

フォルダ作成

typescriptバージョンのnext-authのサンプルを使って修正していきます。
こちらのテンプレートをそのまま使わせていただきます。

自分はjamstack appと名付けました。

$ mkdir jamstack
$ cd jamstack
$ mkdir mysql
$ git clone git@github.com:nextauthjs/next-auth-typescript-example.git app

app 内のgitの紐付けも外し、yarn installでパッケージをインストールしておきます。

$ cd app
$ rm -rf .git
$ yarn install

Prismaのインストール

Prismaをインストールして初期化します。

$ yarn add prisma --dev
$ yarn add @prisma/client

$ npx prisma init

これで.envファイルが生成されるので、ここの環境変数を編集します。
基本的にコンテナの中からアクセスするので、mysqlコンテナの3306ポートを指定します。
(外側からの場合は 127.0.0.1:13306となります。)

DATABASE_URL="mysql://root:root@mysql:3306/jamstack_db"

docker-compose.ymlの作成

ルートディレクトリに docker-compose.ymlを置きます。
mysqlとnext.js用のコンテナを2つ用意します。

docker-compose.yml
version: '3'
services:

    next:
      image: node:15.0.1
      volumes:
        - ./app:/home/app
        - node_modules_volume:/home/app/node_modules
      ports:
        - 3000:3000
      working_dir: /home/app
      command: [bash, -c, yarn upgrade --no-progress --network-timeout 1000000 && yarn run dev]

    mysql:
        build: ./mysql
        environment:
            TZ: Asia/Tokyo
            MYSQL_ROOT_PASSWORD: root
            MYSQL_DATABASE: jamstack_db
        ports:
            - 13306:3306
        volumes:
            - mysql_volume:/var/lib/mysql

volumes:
    mysql_volume:
    node_modules_volume:

mysqlの設定ファイルは以下です。

mysql.cnf
# MySQLサーバーへの設定
[mysqld]
# 文字コード/照合順序の設定
character_set_server=utf8mb4
collation_server=utf8mb4_bin

# タイムゾーンの設定
default_time_zone=SYSTEM
log_timestamps=SYSTEM

# デフォルト認証プラグインの設定
default_authentication_plugin=mysql_native_password

# mysqlオプションの設定
[mysql]
# 文字コードの設定
default_character_set=utf8mb4

# mysqlクライアントツールの設定
[client]
# 文字コードの設定
default_character_set=utf8mb4

mysqlのDockerfileです。

FROM mysql:8.0.21
# FROM mysql@sha256:77b7e09c906615c1bb59b2e9d7703f728b1186a5a70e547ce2f1079ef4c1c5ca

RUN echo "USE mysql;" > /docker-entrypoint-initdb.d/timezones.sql &&  mysql_tzinfo_to_sql /usr/share/zoneinfo >> /docker-entrypoint-initdb.d/timezones.sql

COPY ./my.cnf /etc/mysql/conf.d/my.cnf

Githubの認証の設定

今回はGithubアカウントを使って認証をしたいので、Github Appを作ることになります。Settingsを開きます。

Screen Shot 2021-05-21 at 23.49.24.png

下までスクロールして Developer settingsをクリックします。

Screen Shot 2021-05-21 at 23.49.45.png

ここで New Github Appを作ります。

Screen Shot 2021-05-21 at 23.49.52.png

appの名前はjamstack-appとして、homepage URLはとりあえずgithubのレポジトリのリンクにしておきます。

Screen Shot 2021-05-21 at 23.52.55.png

Callback URLhttp://localhost:3000としておきます。Vercelなどでデプロイしたらまた変更します。

Screen Shot 2021-05-21 at 23.53.18.png

これで Create Appとします。すると次の画面に到達するので、client secretをgenerateします。client secretClient IDは保存しておきます。

Screen Shot 2021-05-21 at 23.56.48.png

環境変数の設定

クローンしたレポジトリに.env.local.exampleがあるはずなので、内容をコピーして .envに追加します。先ほどゲットした変数もペーストしておきます。

NEXTAUTH_URL=http://localhost:3000
GITHUB_ID=[Client ID]
GITHUB_SECRET=[Client secret]

prisma周辺の設定

prismaをインポートするように[...nextauth].tsを編集します。

[...nextauth].ts
import NextAuth from "next-auth"
import Providers from "next-auth/providers"
// 以下を追加
import { PrismaClient } from "@prisma/client";
import Adapters from "next-auth/adapters";
let prisma;

if (process.env.NODE_ENV === "production") {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

...
// adapterを追加
  database: process.env.DATABASE_URL,
  adapter: Adapters.Prisma.Adapter({ prisma }),

このままだとtypescriptに怒られるので、global.d.tsにprismaをanyとして定義しておきます。(もっといい方法を探す余裕が今回ありませんでした、すみません。)

global.d.ts
export {};
declare global {
    namespace NodeJS {
        interface Global {
            prisma: any;
        }
    }
}

マイグレーションする

マイグレーションするときにPrismaのスキーマファイルをここに書きます。

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Account {
  id                 Int       @id @default(autoincrement())
  compoundId         String    @unique @map(name: "compound_id")
  userId             Int       @map(name: "user_id")
  providerType       String    @map(name: "provider_type")
  providerId         String    @map(name: "provider_id")
  providerAccountId  String    @map(name: "provider_account_id")
  refreshToken       String?   @map(name: "refresh_token")
  accessToken        String?   @map(name: "access_token")
  accessTokenExpires DateTime? @map(name: "access_token_expires")
  createdAt          DateTime  @default(now()) @map(name: "created_at")
  updatedAt          DateTime  @default(now()) @map(name: "updated_at")
  @@index([providerAccountId], name: "providerAccountId")
  @@index([providerId], name: "providerId")
  @@index([userId], name: "userId")
  @@map(name: "accounts")
}
model Session {
  id           Int      @id @default(autoincrement())
  userId       Int      @map(name: "user_id")
  expires      DateTime
  sessionToken String   @unique @map(name: "session_token")
  accessToken  String   @unique @map(name: "access_token")
  createdAt    DateTime @default(now()) @map(name: "created_at")
  updatedAt    DateTime @default(now()) @map(name: "updated_at")
  @@map(name: "sessions")
}
model User {
  id            Int       @id @default(autoincrement())
  name          String?
  email         String?   @unique
  emailVerified DateTime? @map(name: "email_verified")
  image         String?
  createdAt     DateTime  @default(now()) @map(name: "created_at")
  updatedAt     DateTime  @default(now()) @map(name: "updated_at")
  @@map(name: "users")
}
model VerificationRequest {
  id         Int      @id @default(autoincrement())
  identifier String
  token      String   @unique
  expires    DateTime
  createdAt  DateTime @default(now()) @map(name: "created_at")
  updatedAt  DateTime @default(now()) @map(name: "updated_at")
  @@map(name: "verification_requests")
}

マイグレーションを行う際には、コンテナに入って行いました。

$ dx jamstack_next_1 bash
root@ea23ae917a29:/home/app# npx prisma migrate dev
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": MySQL database "jamstack_db" at "127.0.0.1:13306"

✔ Enter a name for the new migration: … 20210521
The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20210521153533_20210521/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (2.23.0) to ./node_modules/@prisma/client in 158ms

これでSequel Proなどで確認してみると、dbが作成されているのがわかります。

Screen Shot 2021-05-22 at 0.40.27.png

立ち上げてみる

$ docker-compose up

localhost:3000にて、You are not signed inの状態でアクセスできるはずです。

Screen Shot 2021-05-22 at 0.54.38.png

これでSign inボタンを押すと、いろいろ選択肢が出てくるはずです。今回はGithubの設定しかしていないので、Sign in with Githubを選びます。

Screen Shot 2021-05-22 at 0.54.50.png

そうすると無事ログインできているはずです。

Screen Shot 2021-05-22 at 0.54.56.png

デプロイ

デプロイするときには二つのサービスを使います。

  • Heroku ClearDB (ユーザー、セッションなどの管理)
  • Vercel (Next.jsのアプリをデプロイする)

HerokuでのDBのデプロイ

herokuで適当なアプリを作って、そこにcleardbのアドオンを追加します。そこで出てきたCLEARDB_DATABASE_URLをメモっておいてください。以下の5行でとりあえず終わりです。

$ heroku login
$ heroku git:remote --app database-provisioning
$ heroku apps:create database-provisioning
$ heroku addons:create cleardb:ignite
$ heroku config
CLEARDB_DATABASE_URL=......

Vercelでのデプロイ

Vercelで最後にデプロイします。このときにpackage.jsonを修正して、マイグレーションを行う設定を追加します。あと、npm-run-allを走らせる設定も追加します。

package.json
{
...........
  "homepage": "http://next-auth-typescript-example.now.sh",
  "main": "",
  "scripts": {
    // ココ変更!
    "migrate:deploy": "prisma migrate deploy",
    "dev": "next",
    "build": "npm-run-all migrate:deploy build-app",
    "build-app": "next build",
    "start": "next start",
    "types": "tsc --noEmit"
  },
...........
  // npm-run-all を走らせる
  "devDependencies": {
    "@types/node": "^14.14.41",
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "prisma": "^2.23.0",
    "npm-run-all": "^4.1.5",
    "typescript": "^4.2.4"
  }
}

Vercelでデプロイする際には、DATABASE_URLの環境変数を追加しましょう。

Screen Shot 2021-05-22 at 1.34.48.png

vercelでもデプロイできています!

01:38:57.342    $ npm-run-all migrate:deploy build-app
01:38:57.810    $ prisma migrate deploy
01:38:58.325    Prisma schema loaded from prisma/schema.prisma
01:38:58.358    Datasource "db": MySQL database "heroku_819789418405824" at "us-cdbr-east-03.cleardb.com:3306"
01:39:00.084    1 migration found in prisma/migrations
01:39:02.142    The following migration have been applied:
01:39:02.143    migrations/
01:39:02.143      └─ 20210521153533_20210521/
01:39:02.143        └─ migration.sql
01:39:02.143          
01:39:02.143    All migrations have been successfully applied.
01:39:02.619    $ next build

環境変数の追加

先ほどは追加し忘れた環境変数(NEXTAUTH_URLなど)も追加しておきます。

Screen Shot 2021-05-22 at 1.42.26.png

NEXTAUTH_URLはcanonical urlを貼るようにしてください。以下だと jamstack-greenteabiscuit.vercel...となります。

Screen Shot 2021-05-22 at 2.01.05.png

GithubのCallback URLも更新しておきます。ローカルで開発するときとで分けないといけないのがちょっと面倒です。ローカル用のGithub Appと本番用のGithub Appで分けてもいいかもしれません。

Screen Shot 2021-05-22 at 1.50.36.png

参考

以下が参考になりました。ありがとうございました。

Next.js + Prisma + NextAuth.js + React Query で作るフルスタックアプリケーションの新時代

16
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
25