はじめに
少しキャッチーなタイトルにしました。すみません。
今回は、T3 Stack を使用して、そこそこ便利な日程調整ツール(以下日程でご都合いかがでしょうかメーカー)を作成した方法について解説します。
Google カレンダーと連携した「以下の日程でご都合いかがでしょうかメーカー」になります。
プロダクトの名前は、UnScheduleです。
Google カレンダーと連携した「以下の日程でご都合いかがでしょうかメーカー」の挙動について
最初に、Google カレンダーと連携した「以下の日程でご都合いかがでしょうかメーカー」であるUnScheduleの挙動について解説します。
使い方は非常にシンプルで、Google ログインを行った後に、カレンダーの期間を選択するだけです。
自動的にカレンダーの空いている時間を提案する、『以下の日程でご都合いかがでしょうか』の文章を作成してくれます。
就業時間の設定や、休日の設定等を行うことが出来ます。
上記の動作で出来上がった文章が以下になります。
以下の日程でご都合いかがでしょうか。
10月21日(月)09:00〜12:00, 13:00〜13:30, 14:00〜17:00
10月22日(火)09:00〜10:30, 11:30〜12:30, 13:00〜16:00
10月23日(水)09:00〜10:00, 11:45〜12:30, 13:00〜17:00
10月24日(木)09:00〜12:30, 13:00〜13:30, 15:15〜17:00
作成した経緯
社内メンバーとの話し合いで、「日程調整って、基本的にはGoogleカレンダーの空いている時間を提案しているだけだよね」という話題が出たため、とりあえず試しに作ってみることにしました。
以前、エンジニア向けのタイピングゲームを作成した際に、社内用に作成したT3 Stackのテンプレートがあったため、今回はそれをそのまま流用してプロダクトを作成しました。
技術スタック
T3 Stack の構成で作成しました。
- Next.js
- TypeScript
- tRPC
- Prisma
- TailwindCSS
- Auth.js
という構成ですね。
データベースには Supabase を使用し、デプロイ先は Vercel に設定しています。今回のプロジェクトでは、Next.js の App Router を採用しました。
開発した機能について
以下に、今回のプロダクト(Google カレンダーと連携した「以下の日程でご都合いかがでしょうかメーカー」)を作成する際に開発した機能についてまとめていきます。
Google ログイン機能
Google のログイン機能を作成しました。
フロントの認証情報の管理は、NextAuth.js
に任せています
adapter
にPrismaAdapter
を設定することで、認証情報のデータを Prisma を介して丸っとデータベース(今回は Supabaseを利用しています)に格納しています。
adapter: PrismaAdapter(db) as Adapter
Google カレンダーの空いている時間帯を取得する機能
ユーザーが選択した期間における、Google カレンダーの空いている時間を取得する機能を作成しました。
以下のコードです。
const auth = new google.auth.OAuth2({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
});
const accountFromDb = await db.account.findFirst({
where: { userId: ctx.session.user.id },
select: { id: true, access_token: true, expires_at: true },
});
auth.setCredentials({
access_token: accountFromDb?.access_token,
});
const calendar = google.calendar({ version: 'v3', auth: auth });
const timeZone = (await calendar.settings.get({ setting: 'timezone' })).data.value;
const response = await calendar.freebusy.query({
requestBody: {
timeMin: startDate,
timeMax: endDate,
items: [{ id: 'primary' }],
timeZone,
},
});
こちらのコードで、Google カレンダーの空いている時間を取得しました。
上記のコードについて、ざっくりと解説します。
1. OAuth2を使った認証の設定
const auth = new google.auth.OAuth2({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
});
ここでは、GoogleのOAuth2クライアントを初期化しています。環境変数に保存されたクライアントIDとクライアントシークレットを使って、GoogleのAPIにアクセスするための認証情報を設定します。
2. データベースからアクセストークンを取得
const accountFromDb = await db.account.findFirst({
where: { userId: ctx.session.user.id },
select: { id: true, access_token: true, expires_at: true },
});
次に、データベースから、現在ログインしているユーザーのアクセストークンとその期限を取得しています。このトークンは、Google Calendar APIに対してリクエストを行うために必要なものです。
3. アクセストークンの設定
auth.setCredentials({
access_token: accountFromDb?.access_token,
});
ここで、取得したアクセストークンをOAuth2クライアントに設定します。これにより、Google Calendar APIに対して認証されたリクエストが行えるようになります。
4. Google Calendar APIクライアントの初期化
const calendar = google.calendar({ version: 'v3', auth: auth });
Google Calendar APIのクライアントを作成しています。このクライアントを使って、カレンダーに関する操作(例: 空き時間の確認など)を行うことができます。
5. ユーザーのタイムゾーンを取得
const timeZone = (await calendar.settings.get({ setting: 'timezone' })).data.value;
Google Calendar APIの設定を利用して、ユーザーのタイムゾーン情報を取得します。この情報は、後のクエリで適切な時間を扱うために使います。
6. 空き時間のクエリ
const response = await calendar.freebusy.query({
requestBody: {
timeMin: startDate,
timeMax: endDate,
items: [{ id: 'primary' }],
timeZone,
},
});
この部分では、Google Calendar APIのfreebusy.query
を使って、指定された期間(startDate
とendDate
)のユーザーの空き時間を問い合わせます。items
でprimary
を指定しているため、ユーザーのメインカレンダーが対象になります。タイムゾーン情報も含めてリクエストを行い、結果がresponse
に返されます。
悩んだこと
Supabaseのスペックを抑えたのが原因か、tRPCの利用が影響しているのかは不明ですが、APIのレスポンスがかなり遅いです。
また、Google カレンダーの データを取得する API も、若干遅いです。
そのため、日程を選択した後に 3 秒ほど待たされることになります。
待ち時間を短縮するため、ページ読み込み時にあらかじめ1ヶ月分のカレンダーデータを取得するように修正しました。その結果、日程調整のテキストが爆速で生成されるようになりました。
削った機能
複数の期間を選択する機能は削除しました。最終的には日程調整用のテキストが生成されるだけなので、不要な部分は手動で消す方が、ユーザー体験が向上すると判断したためです。
また、特定の時間(昼休みなど)を除外する機能も検討しましたが、実装は見送りました。Google カレンダーにその時間を予定として入れてもらう方がシンプルで、ユーザーにもわかりやすいと考えたからです。
終わりに
今回は、Google カレンダーと「以下の日程でご都合いかがでしょうかメーカー」である、UnScheduleの技術スタックと実装について紹介しました。
是非、一度使用して頂ければと思います。
お疲れさまでした。