← Noteトップに戻る
Note公開 2026年2月6日更新 2026年2月6日5 min read

Google Tasks API 使ってみる

Google Tasks API を REST で扱うときに、最初にハマりやすい点を含めて、実際に操作できる形でまとめる。 対象は公式の REST Reference とその関連ページ(認可・Quickstart)です。 REST Reference: スコープ: Quickstart (JavaScript): リソ…

Google Tasks API を REST で扱うときに、最初にハマりやすい点を含めて、実際に操作できる形でまとめる。
対象は公式の REST Reference とその関連ページ(認可・Quickstart)です。

1. まず押さえる全体像

Google Tasks API のサービスエンドポイントは次。

https://tasks.googleapis.com

主要リソースは2つだけ。

  • tasklists: タスクリストの単位(例: 仕事 / 個人)
  • tasks: 各タスクリスト配下のタスク

代表的な HTTP パス:

  • リスト一覧: GET /tasks/v1/users/@me/lists
  • タスク一覧: GET /tasks/v1/lists/{tasklist}/tasks
  • タスク作成: POST /tasks/v1/lists/{tasklist}/tasks
  • タスク更新(部分): PATCH /tasks/v1/lists/{tasklist}/tasks/{task}
  • タスク更新(全体): PUT /tasks/v1/lists/{tasklist}/tasks/{task}
  • タスク削除: DELETE /tasks/v1/lists/{tasklist}/tasks/{task}
  • タスク移動: POST /tasks/v1/lists/{tasklist}/tasks/{task}/move

2. 認証とスコープ

REST 呼び出しには OAuth 2.0 のアクセストークン(Bearer)が必要。

用途ごとのスコープ:

  • 読み取りのみ: https://www.googleapis.com/auth/tasks.readonly
  • 作成/更新/削除あり: https://www.googleapis.com/auth/tasks

最小権限の原則で、最初は tasks.readonly から始めるのが安全。

3. まずは最短で叩く(curl)

以下では ACCESS_TOKEN を取得済みとして説明。

export ACCESS_TOKEN='ya29...'

3-1. タスクリスト一覧を取る

curl -s \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://tasks.googleapis.com/tasks/v1/users/@me/lists"

3-2. あるリストのタスク一覧を取る

TASKLIST_ID='xxxxx'

curl -s \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks?maxResults=100&showCompleted=true&showHidden=true"

tasks.list はフィルタが多い。

  • 期間系: dueMin, dueMax, completedMin, completedMax, updatedMin
  • 表示系: showCompleted, showDeleted, showHidden, showAssigned
  • ページング: maxResults(最大100), pageToken

補足: 公式では、tasks.list はデフォルトでは割り当てタスク(Docs / Chat Spaces 由来)を返さないと明記されている。必要なら showAssigned=true

3-3. タスク作成

curl -s -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "APIで作ったタスク",
    "notes": "本文メモ"
  }' \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks"

親子関係や並び順を指定するときは、クエリで parentprevious を使える。

3-4. タスクを部分更新(PATCH)

TASK_ID='yyyyy'

curl -s -X PATCH \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "タイトルだけ変更"
  }' \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks/${TASK_ID}"

PATCH は部分更新。1項目だけ変えたいときは基本これで十分。

3-5. タスク完了にする

curl -s -X PATCH \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "completed"
  }' \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks/${TASK_ID}"

未完了へ戻すなら status: "needsAction"

3-6. タスク削除

curl -s -X DELETE \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks/${TASK_ID}"

3-7. タスクの順序/階層を移動

curl -s -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://tasks.googleapis.com/tasks/v1/lists/${TASKLIST_ID}/tasks/${TASK_ID}/move?parent=PARENT_TASK_ID&previous=PREV_TASK_ID"
  • 先頭に置くなら previous を省略
  • トップレベルに戻すなら parent を省略

4. Task リソースで知っておくと得する点

tasks リソースの主なフィールドは title, notes, status, due, completed, deleted, hidden など。

実務で重要なのは次。

  • due は日付情報中心で、時刻は API 設定時に保持されない(時間まで厳密管理する用途には不向き)
  • position は読み取り専用。順序変更は move を使う
  • hidden は読み取り専用。clear 実行後の完了タスク表示に関係
  • assignmentInfo は割り当てタスク向けのコンテキスト情報

5. よくある落とし穴

  • PATCH ではなく PUT を使って意図せず全体更新してしまう
  • 完了タスクが見えず「消えた」と誤解する(showHidden / showCompleted を確認)
  • 参照だけのつもりで tasks スコープを要求してしまう(最小権限にする)
  • タスクIDではなくリストIDを渡して 404 になる(tasklisttask の取り違え)

6. 実装方針のおすすめ

  • 初期は tasklists.listtasks.list だけで読み取り機能を完成させる
  • 更新系は PATCH を中心にして差分更新に寄せる
  • ページング(nextPageToken)を先に実装しておく
  • 割り当てタスクを扱うなら showAssignedassignmentInfo を設計に含める

7. Next.js で使うなら(実装サンプル)

ここでは「ブラウザから直接 Google API を叩かず、Next.js サーバー経由で叩く」構成にする。
アクセストークンはサーバー側で管理する前提。

7-1. 共通ヘルパー(App Router / Pages Router どちらでも使える)

src/lib/googleTasks.ts みたいな場所に置く想定。

const TASKS_BASE_URL = "https://tasks.googleapis.com/tasks/v1";

export async function listTasks(params: {
  accessToken: string;
  tasklistId: string;
  showCompleted?: boolean;
}) {
  const { accessToken, tasklistId, showCompleted = true } = params;
  const query = new URLSearchParams({
    maxResults: "100",
    showCompleted: String(showCompleted),
    showHidden: "true",
  });

  const res = await fetch(
    `${TASKS_BASE_URL}/lists/${tasklistId}/tasks?${query.toString()}`,
    {
      headers: { Authorization: `Bearer ${accessToken}` },
      cache: "no-store",
    }
  );

  if (!res.ok) {
    const body = await res.text();
    throw new Error(`Google Tasks list failed: ${res.status} ${body}`);
  }

  return res.json();
}

export async function insertTask(params: {
  accessToken: string;
  tasklistId: string;
  title: string;
  notes?: string;
}) {
  const { accessToken, tasklistId, title, notes } = params;
  const res = await fetch(`${TASKS_BASE_URL}/lists/${tasklistId}/tasks`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ title, notes }),
  });

  if (!res.ok) {
    const body = await res.text();
    throw new Error(`Google Tasks insert failed: ${res.status} ${body}`);
  }

  return res.json();
}

7-2. App Router: Route Handler 例

src/app/api/google-tasks/[tasklistId]/route.ts

import { NextRequest, NextResponse } from "next/server";
import { insertTask, listTasks } from "@/lib/googleTasks";

function getAccessTokenFromServerSession() {
  // 実際は DB / セッションストアから取り出す
  const token = process.env.GOOGLE_ACCESS_TOKEN;
  if (!token) throw new Error("GOOGLE_ACCESS_TOKEN is missing");
  return token;
}

export async function GET(
  _req: NextRequest,
  { params }: { params: { tasklistId: string } }
) {
  try {
    const accessToken = getAccessTokenFromServerSession();
    const data = await listTasks({
      accessToken,
      tasklistId: params.tasklistId,
      showCompleted: true,
    });
    return NextResponse.json(data);
  } catch (e) {
    return NextResponse.json(
      { error: e instanceof Error ? e.message : "unknown error" },
      { status: 500 }
    );
  }
}

export async function POST(
  req: NextRequest,
  { params }: { params: { tasklistId: string } }
) {
  try {
    const body = (await req.json()) as { title?: string; notes?: string };
    if (!body.title) {
      return NextResponse.json({ error: "title is required" }, { status: 400 });
    }

    const accessToken = getAccessTokenFromServerSession();
    const data = await insertTask({
      accessToken,
      tasklistId: params.tasklistId,
      title: body.title,
      notes: body.notes,
    });

    return NextResponse.json(data, { status: 201 });
  } catch (e) {
    return NextResponse.json(
      { error: e instanceof Error ? e.message : "unknown error" },
      { status: 500 }
    );
  }
}

7-3. Pages Router: API Route 例

src/pages/api/google-tasks/[tasklistId].ts

import type { NextApiRequest, NextApiResponse } from "next";
import { insertTask, listTasks } from "@/lib/googleTasks";

function getAccessTokenFromServerSession() {
  const token = process.env.GOOGLE_ACCESS_TOKEN;
  if (!token) throw new Error("GOOGLE_ACCESS_TOKEN is missing");
  return token;
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const tasklistId = req.query.tasklistId as string;
    const accessToken = getAccessTokenFromServerSession();

    if (req.method === "GET") {
      const data = await listTasks({ accessToken, tasklistId });
      return res.status(200).json(data);
    }

    if (req.method === "POST") {
      const { title, notes } = req.body as { title?: string; notes?: string };
      if (!title) return res.status(400).json({ error: "title is required" });

      const data = await insertTask({ accessToken, tasklistId, title, notes });
      return res.status(201).json(data);
    }

    return res.status(405).json({ error: "Method Not Allowed" });
  } catch (e) {
    return res.status(500).json({
      error: e instanceof Error ? e.message : "unknown error",
    });
  }
}

7-4. クライアント側から呼ぶ例

// タスク一覧取得
const listRes = await fetch(`/api/google-tasks/${tasklistId}`);
const listData = await listRes.json();

// タスク追加
await fetch(`/api/google-tasks/${tasklistId}`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "買い物する", notes: "牛乳" }),
});

7-5. Next.js 実装での注意点

  • Google の access_token / refresh_tokenNEXT_PUBLIC_ で公開しない
  • トークン更新はサーバー側で実行する(必要なら OAuth クライアントで refresh)
  • GET 側は cache: "no-store"revalidate を明示して古いタスク表示を防ぐ
  • まずは API Route で薄い BFF を作ってから UI を繋ぐと安全

参照した公式ページ(2026-02-05 時点で確認):

Xでシェア

関連記事

読み合わせて深掘りする