Google Calendar API をアプリに統合する方法
公開日
このガイドでは、Google Calendar API をアプリに統合する手順をステップごとに説明します。Google Cloud プロジェクトの設定、必要なスコープ、よくある注意点、そして実際の認可フローの例を取り上げます。
前提条件
このガイドは、メールアドレス、Google Cloud プロジェクトの設定時に使えるドメイン、ある程度のコーディング経験、そして作りたいもののおおまかなイメージがあることを前提にしています。
Google Calendar API を使ったことがなく、アプリに追加するために必要な手順を一つずつ理解したい方にも役立ちます。
Google Calendar API をアプリで利用・統合する手順
1. Google Developer Console に登録する
Google Developer Console アカウントをお持ちでない場合は、https://console.cloud.google.com/ で作成してください。
2. 既存の Google Cloud プロジェクトを選択または新規作成する
Google Cloud では、開発者や組織が複数のプロジェクトを持てます。以下の手順を進める前に、正しいプロジェクトを選択していることを確認してください。
画面左上のプロジェクトドロップダウンをクリックします。通常、名前はプロジェクト名と一致します。
既存プロジェクトを選択するか、モーダル右上の 「New Project」 をクリックして新規プロジェクトを作成します。
3. Google Calendar API サービスを有効化する
アカウントを用意し、正しいプロジェクトにいることを確認したら、次の手順で Google Calendar API サービスを有効にします。
- Google Cloud Console にアクセス
- 「APIs & Services」 をクリック

- 「Enable APIs and services」 をクリック

- 「Google Calendar API」を検索

- 「Enable」をクリックしてサービスを有効化

4. OAuth 同意画面を設定する
Google Calendar API サービスを有効化したら、次は OAuth 同意画面の設定です。これは、エンドユーザーがカレンダーをアプリに接続するときに表示される画面です。通常、アプリのロゴ、アプリ名、要求する権限などが表示されます。
- 「OAuth Consent Screen」タブをクリック

- 「Get Started」をクリック

- 「 App Information 」セクションを入力。 アプリ名とカスタマーサポート用メールアドレスを入力します。

- 対象ユーザーを選択。 internal は自社組織内のユーザーのみ、 external は一般の Google アカウントも接続できます。

- 連絡先情報を入力。 プロジェクトの変更に関する Google からの通知を受け取るメールアドレスを入力します。

- 「Google API Services: User Data Policy」に同意 チェックボックスをオンにします。

- 「Create」 をクリック

5. OAuth クライアントを作成する
OAuth 同意画面を設定したら、プロジェクト用の OAuth クライアントを作成できます。「Clients」タブで 「Create Client」 をクリックするか、概要ページの 「Create OAuth Client」 をクリックしてください。
アプリが動作するプラットフォームごとにクライアントを作成できます。たとえば Web アプリと iOS アプリを作る場合は、それぞれに別の OAuth クライアント ID が必要です。この例では「Web Application」クライアントを「Web Client」という名前で作成します。
同じフローの中で、Authorized JavaScript origins と Authorized redirect URIs も設定します。
Authorized JavaScript origins には、例として myapp.domain.com のようにアプリをホストするドメインを入力します。
Authorized redirect URIs には、Google での認証後にユーザーをリダイレクトする URL をすべて入力します。Google はこの URL に認可コードを付加するため、URL にはプロトコルを含める必要があります。
必要事項を入力したら「Create」をクリックします。Google がモーダルを開き、Client ID と Client Secret を表示します。両方の値をコピーして安全な場所に保管してください(通常は .env ファイルです。後述の認証フローで使用します)。JSON ファイルをダウンロードして 1Password などのシークレットマネージャに保存することもできます。
6. テストユーザーを追加して動作確認する
ローカルで開発している間は、テストユーザーを追加しない限り Google Calendar アカウントを接続できません。アプリが外部向けで、まだ Google の承認を受けていないためです。
テストユーザーを追加する手順は次のとおりです。
- 「Audience」タブをクリック

- 「Test Users」セクションまでスクロール

- 「Add Users」をクリックし、メールアドレスを入力

7. 使用予定のカレンダースコープを追加する
Google Calendar 統合で解決したいユースケースに応じて、ユーザーが Google Calendar アカウントを認可するときに要求するスコープは変わります。
スコープは、ユーザーに求める権限だと考えてください。アプリがユーザーの Google アカウントの非公開データにアクセスするために必要です。たとえば、カレンダーの一覧取得やカレンダーイベントの読み取りなどがあります。
Google はスコープを機密 (sensitive) と非機密 (non-sensitive) に分けています。機密スコープを追加する場合は、アプリを審査に提出する必要があります。すでに審査済みのアプリに機密スコープを追加する場合も同様です。
スコープを管理する手順は次のとおりです。
- 「Data Access」タブをクリック

- 「Add or remove scopes」をクリック

- 名前または値でスコープを検索し、追加

8. Google Calendar API に慣れる
Google クライアントの設定が終わり、ユーザーがカレンダーを接続できるようになったら、次は Google Calendar API そのものに慣れる番です。Google Calendar API 概要ページ を読み、Events や Calendars などの主要なエンドポイントを確認することをおすすめします。
9. 複数プロバイダーを 1 つの API で扱える Unified Calendar API の利用
Google Calendar だけを統合したい場合は、このステップをスキップして構いません。そうでなければ、すべてのカレンダープロバイダーを 1 つの API で扱える Unified Calendar API の利用をおすすめします。
Unified Calendar API を使えば、すべてのプロバイダーに対して 1 つの統合を構築し、保守するだけで済みます。後から Outlook や iCloud に対応することになっても、新しい統合を書かずに追加できます。
また、複数の統合を保守したり、破壊的変更に対応したり、各プロバイダーの API の細かい仕様を学んだりする手間も省けます。
Google Calendar 認可フローの例
以下のフローチャートは、ユーザーが Google Calendar をアプリに接続するためのシンプルな OAuth フローを示しています。
Google は Node.js や Python などのクライアントライブラリを提供していますが、ここでは簡潔にするため、HTTP 呼び出しと TypeScript だけで認可フローを説明します。
クライアント側 (UI)
最初はクライアント (UI) 側です。ここでは「Connect Google Calendar」ボタンを表示します。
const googleOauthUrl = getGoogleOAuthUrl()
<button href="googleOauthUrl" rel="noopener noreferrer"> Connect Google Calendar </button>
Google OAuth URL を組み立てるユーティリティ関数を用意することをおすすめします。コードが読みやすくなり、クライアントの状態や強制的な同意などのパラメータも渡しやすくなります。
const SCOPES = [
"openid",
"email",
"<https://www.googleapis.com/auth/calendar.calendarlist>",
"<https://www.googleapis.com/auth/calendar.events>",
"<https://www.googleapis.com/auth/calendar.readonly>",
// add more scopes as needed
];
export interface ClientState {
session: Session;
returnUrl?: string;
}
export function stateToB64(session: ClientState): string {
return encode(JSON.stringify(session));
}
export function getGoogleOAuthUrl(
state: ClientState,
) {
const params = new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID || "",
redirect_uri: `${getHostName()}/api/connect/google`, // change the redirect URL as needed
response_type: "code",
scope: SCOPES.join(" "),
prompt: "consent",
access_type: "offline",
state: stateToB64(state),
});
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}
prompt パラメータは none、consent、select_account のいずれかを指定できます。
- consent : ユーザーがすでに認可済みでも、認可画面をもう一度表示します。新しいスコープを追加したときや、ユーザーが必要なスコープをすべて許可していないときに便利です。
- select_account : ユーザーにアカウントの選択を求めます。
- none : 認証画面や同意画面を表示しません。
API 側 (バックエンド)
次に API ハンドラーを作成します。Google からコードとスコープを受け取り、コードをトークンと交換する役割を担います。この例では検証に zod を使用しています。
import { z } from "zod";
const successSchema = z.object({
code: z.string(),
scope: z.string(),
state: z.string(),
});
const errorSchema = z.object({
error: z.string(),
});
type ErrorParams = z.infer<typeof errorSchema>;
const querySchema = z.union([successSchema, errorSchema]);
// Handler
const googleHanlder: NextApiHandler = async (req, res) => {
try {
const result = querySchema.parse(req.query);
if (isError(result)) {
const q = new URLSearchParams({
error: "ACCESS_DENIED",
});
return res.redirect(`/?${q}`);
}
const { session, returnUr } = stateFromB64(
result.state
);
if (!hasRequiredScopes(result.scope)) {
const q = new URLSearchParams({
error: "MISSING_REQUIRED_PERMISSIONS",
});
return res.redirect(`/?${q}`);
}
const { access_token, refresh_token, id_token, expires_in } =
await exchangeCodeForTokens(result.code);
const { email } = decodeIdToken(id_token);
// Update or insert the calendar connection, depending on your use case
const connection = await upsertConnection(
{
email,
accessToken: access_token,
refreshToken: refresh_token,
expiresInSeconds: expires_in,
status: ConnectionStatus.ACTIVE,
provider: CalendarProvider.GOOGLE,
scopes: result.scope,
reminderCount: 0,
lastRemindedAt: null,
},
session.user
);
const q = new URLSearchParams({
cid: connection.id,
});
if (returnUrl) q.append("returnUrl", returnUrl);
// The API redirects back to the client side, returning the connection, or errors if any
res.redirect returnUrl ? returnUrl : `/calendars/google?${q}`
);
} catch (e: any) {
let error = JSON.stringify(e);
const querystr =
typeof req.query === "string" ? req.query : JSON.stringify(req.query);
console.error("Error in googleHandler", querystr);
console.error("Failed to connect Google account", e);
const q = new URLSearchParams({
error,
});
return res.redirect(`/?${q}`);
}
};
クライアント側と同様に、コードとトークンの交換、ID トークンのデコード、base64 からの状態の復元などは、小さなユーティリティ関数にまとめることをおすすめします。
export async function exchangeCodeForTokens(code: string) {
const data = new FormData();
data.append("code", code);
data.append("client_id", process.env.GOOGLE_CLIENT_ID || "");
data.append("client_secret", process.env.GOOGLE_CLIENT_SECRET || "");
data.append("redirect_uri", `${getHostName()}/api/connect/google`); // your URL
data.append("grant_type", "authorization_code");
try {
const result = await fetch("<https://oauth2.googleapis.com/token>", {
method: "POST",
body: data,
});
const json = await result.json();
if (json.error) throw json;
const parsed = responseSchema.parse(json);
return parsed;
} catch (e) {
console.error("Exchange failed");
throw e;
}
}
export function decodeIdToken(idToken: string) {
const data = jwt.decode(idToken);
if (typeof data === "string" || !data?.email) {
throw new Error(`Could not parse id_token: ${idToken}`);
}
return data;
}
function isError(query: Record<string, any>): query is ErrorParams {
return Boolean(query.error);
}
export function stateFromB64(encoded: string): ClientState {
const str = decode(encoded);
return JSON.parse(str) as ClientState;
}
Google Calendar API 統合時の注意点
- 審査には数週間かかることがあるため、事前に計画を立てる。 承認プロセスには時間がかかり、最初の提出で却下されることもよくあります。この遅延をスケジュールに組み込み、関係者全員に共有しておきましょう。
- Webhook は約 24 時間で失効するため、必ず更新する。 カレンダーの変更を検知するために Webhook を登録できますが、約 24 時間で期限切れになります。Cron ジョブで 20 分以内に失効する Webhook を見つけて更新すると安全です。
- 必要なスコープだけを要求する。 Google は審査が非常に厳しく、余分なスコープは却下の原因になります。また、アプリの機能に合わない権限を求められると、ユーザーは承認をためらいます。
- クォータとレート制限に対応する。 Google にはプロジェクトごとの 1 分あたりのクォータと、プロジェクトおよびユーザーごとの 1 分あたりのクォータがあります。どちらかを超えると 403 usageLimits または 429 rateLimitExceeded が返されます。指数バックオフなどを使って制限を超えないようにしてください。
- 個人データをログに出力しない。 ログ自体は問題ありませんが、イベントには説明や参加者のメールアドレスなどの個人情報が含まれるため、事前に取り除いてください。
Apiroc Unified Calendar API で複数のカレンダープロバイダーを統合する
私たちのチームは、Google Calendar、Outlook、iCloud Calendar と長年向き合い、数十億回の API 呼び出しを処理するカレンダー統合を構築してきました。複数のプロバイダーを統合し、各 API の仕様を学び、それぞれの実装を安定して動かし続けるために何が必要かを理解しています。
だからこそ、Google Calendar、Microsoft Outlook、iCloud Calendar を最初からサポートする Unified Calendar API として Apiroc を作りました。堅牢で使いやすい 1 つの API で、主要なプロバイダーをすべて統合できます。
無料で開始して、最大 10 件の End User Account で API を試し、機能を確認してから、必要に応じて後からアップグレードできます。クレジットカードは不要です。
よくある質問
テストユーザーを追加する必要があるのはなぜですか?
未審査の外部アプリは、開発中で Google の承認を受ける前の段階では、ホワイトリストに登録したテストアカウントでしか利用できません。
Google カレンダーの Webhook はどのくらい有効ですか?
プッシュ通知のサブスクリプションは約 24 時間で期限切れになります。そのため、バックエンドで期限が切れる前に更新する必要があります。
後で Outlook や iCloud のカレンダーを簡単に追加する方法はありますか?
はい。Apiroc のような Unified Calendar API を使えば、Google、Outlook、iCloud を 1 つの API で統合できます。個別の統合を構築して保守する時間とコストを節約できます。
バックグラウンドジョブには “offline_access” が必要ですか?
はい。同意 URL に access_type=offline を付けると Google がリフレッシュトークンを返すため、ユーザーが離席中でもサーバーから API を呼び出せます。
