Node.jsからGoogle Calendarの予定を取得する

公開日: 

やりたいこと

Node.js でコードを実行して、Google API を通じてカレンダーから予定を取得したい。最終的には定期的にコードを実行して毎週月曜日に 1 週間分予定とかを取得したいけど、一旦それは置いておく。

認証情報の取得

公式ドキュメント

https://developers.google.com/calendar/api/guides/overview?hl=ja

ドキュメントに従って認証の情報を作成

Node.js のクイックスタート

https://developers.google.com/calendar/api/quickstart/nodejs?hl=ja

に従いながら、『デスクトップ アプリケーションの認証情報を承認する』までを完了する。このクイックスタートはブラウザを使って認証を完了する方法なので、サーバーからリクエストを送りたいというゴールとは違ってきます。ダウンロードしたcredentials.jsonの中身の情報を使っていきます。

リフレッシュトークンの取得

サーバーからリクエストを送る際の認証にアクセストークンを用います。

ただ、アクセストークンは有効な期間が定まっており、おそらく Google API では 1 時間です。

これでは、アクセストークンの期限が切れるたびに、リフレッシュトークンを用いて手動で発行し直さなければなりません。

ただ、googleapi の npm パッケージでは、リフレッシュトークンを設定しておくと自動でアクセストークンの再発行を行ってくれるので、今回はこれを利用します。

以下の記事を参考にしました。

https://cloud.google.com/apigee/docs/api-platform/security/oauth/access-tokens?hl=ja

https://blog.shinonome.io/google-api/

認可コードの取得:100:

Google 公式ドキュメント

https://cloud.google.com/apigee/docs/api-platform/security/oauth/access-tokens?hl=ja#requestinganauthorizationcode

リフレッシュトークンを取得するにはまず認可コードを取得します。

必要なものはcredentials.jsonの中身の

  • クライアント ID

です。

ブラウザで

https://accounts.google.com/o/oauth2/auth?
client_id=取得したクライアントID
&redirect_uri=http://localhost <= なんでもいいのでlocalhost
&scope=https://www.googleapis.com/auth/calendar.readonly
&access_type=offline
&response_type=code

この URL にアクセスします。すると、OAuth 2.0 サーバーはユーザーを認証し、アプリケーションが要求するスコープにアクセスするために、ユーザーの同意を得る必要があります。そのためにリダイレクトされた先の URL に含まれる『code』というクエリパラメーターが認可コードの値です。

アクセセストークン(リフレッシュトークン)の取得:monocle_face:

curl \
-d code=さっき取得した認可コード \
-d client_id=最初に取得したクライアントID \
-d client_secret=最初に取得したクライアントシークレット \
-dredirect_uri=http://localhost \
-d grant_type=authorization_code \
https://accounts.google.com/o/oauth2/token

curl コマンドを使ってリクエストを送ります。すると

{
  "access_token": アクセストークン,
  "expires_in": 3599,
  "refresh_token": リフレッシュトークン,
  "scope": "https://www.googleapis.com/auth/calendar.readonly",
  "token_type": "Bearer"
}

このようなレスポンスが返ってきます。これでリフレッシュトークン取得完了です。

次は今取得した

  • クライアント ID
  • クライアントシークレット
  • リフレッシュトークン

を用いて Node.js でコードを実行してみましょう!

サンプルコード

ライブラリの追加

https://github.com/googleapis/google-api-nodejs-client

このライブラリをインストールしましょう。自分は 2024 年 1 月 7 日時点でバージョン130.0.0を使用しています。

サンプルコード

import dotenv from 'dotenv';
import { google } from 'googleapis';

dotenv.config();

// 環境変数から認証情報を取得
const clientId = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
const refreshToken = process.env.GOOGLE_OAUTH_REFRESH_TOKEN;
const calendarId = process.env.CALENDAR_ID;

// 認証のための関数
const getGoogleOAuth = async () => {
  const googleOAuth = new google.auth.OAuth2(
    clientId,
    clientSecret,
    'http://localhost',
  );

  // 毎回のリクエスト時に新しいアクセストークンを取得
  googleOAuth.setCredentials({
    refresh_token: refreshToken,
  });

  try {
    const accessTokenResponse = await googleOAuth.getAccessToken();

    const accessToken = accessTokenResponse.token;

    if (!accessToken)
      throw new Error('有効なアクセストークンを取得できませんでした');

    googleOAuth.setCredentials({
      access_token: accessToken,
    });

    return googleOAuth;
  } catch (err) {
    console.log('エラーの中身', err);
    throw new Error('アクセストークン取得時にエラーが発生しました');
  }
};

/**
 * @param timeMin new Date().toISOString() etc...
 * @param timeMax new Date().toISOString() etc...
 */
export const getEventListFromGoogleCalendar = async (
  timeMin: string,
  timeMax: string,
) => {
  try {
    // 認証
    const googleOAuth = await getGoogleOAuth();

    const calendar = google.calendar({ version: 'v3', auth: googleOAuth });

    const res = await calendar.events.list({
      calendarId,
      timeMin,
      timeMax,
      timeZone: 'Asia/Tokyo',
    });

    if (!res.data.items)
      throw new Error('正常にイベントを取得できませんでした');

    return res.data.items;
  } catch (err) {
    console.log('カレンダーからイベント取得時にエラーが発生しました', err);
  }
};

カレンダー ID

参照するカレンダーの ID も必要になります。

https://qiita.com/mikeneko_t98/items/60e264941492d0b44fe5

こんな感じで調べることができます。

リフレッシュトークンを oauth2Client にセットする

googleOAuth.setCredentials({
  refresh_token: refreshToken,
});

この部分でリフレッシュトークンを設定することで、アクセストークンの再生成を自動でやってくれるようになります。

実行結果

[
  {
    kind: 'calendar#event',
    etag: '"hoge"',
    id: 'hoge',
    status: 'confirmed',
    htmlLink: 'hoge',
    created: '2023-12-27T11:47:23.000Z',
    updated: '2023-12-27T11:48:21.703Z',
    summary: '予定の名前',
    colorId: '5',
    creator: { email: 'hoge@gmail.com', self: true },
    organizer: { email: 'hoge@gmail.com', self: true },
    start: { dateTime: '2024-01-13T11:00:00+09:00', timeZone: 'Asia/Tokyo' },
    end: { dateTime: '2024-01-13T15:00:00+09:00', timeZone: 'Asia/Tokyo' },
    recurrence: ['RRULE:FREQ=MONTHLY;BYDAY=2SA'],
    iCalUID: 'hoge@google.com',
    sequence: 1,
    reminders: { useDefault: true },
    eventType: 'default',
  },
];

こんな感じで予定が取れました!

最後に

結構むずかった…

では

Bye