概要
タイムラインの投稿に「読んでいる本・漫画」の書影と情報を引用カード形式で添付できる機能を実装した。楽天ブックスAPIから書籍情報を取得し、タイトル・著者名・書影・購入リンクをカード表示する。

背景
タイムラインには既にゲーム情報やアニメ情報をカード形式で添付する機能があった。同じパターンで、日常的に読む書籍や漫画の情報も記録できるようにしたかった。書影と購入リンクが自動生成されると、見た目の情報密度が上がり、後から見返したときにも読みやすい。
実装の流れ
1. バックエンド: 楽天ブックスAPIとの連携
楽天ブックスAPIを使う検索エンドポイントを新設した。
環境変数 RAKUTEN_APP_ID にアプリケーションIDを設定し、タイトル文字列を受け取って最大5件の書籍情報を返す。
返却するデータは以下の通り:
| フィールド | 内容 |
|---|---|
title |
書籍名 |
author |
著者名 |
coverUrl |
表紙画像URL(大サイズ優先) |
isbn |
ISBN(13桁の数字のみ) |
itemUrl |
楽天ブックスの商品ページURL |
amazonUrl |
ISBNから自動生成したAmazon検索URL |
セキュリティ面では以下の対策を施した:
- セッションCookieによる認証チェック(未認証リクエストは401を返す)
- 検索クエリの長さ制限(200文字以内)
- レスポンスの各フィールドを型ごとに検証・サニタイズしてから返却
- 画像URLはhttps://で始まるもののみ許可
2. データ保存: D1スキーマの更新
投稿データを管理するテーブルに book_info カラム(TEXT型、NULL可)を追加するマイグレーションを作成した。
未移行環境でも動作するよう、投稿作成・更新APIのどちらにも ALTER TABLE ... ADD COLUMN IF NOT EXISTS パターンでカラムの自動追加処理を入れている。
書籍情報の保存時は、受信したJSONを一度パースして必須フィールドの存在確認と型チェックを行い、安全なフィールドのみを再シリアライズしてDBに保存する。URLフィールドは https:// で始まるかどうかを正規表現で検証し、AmazonのURLは amazon.co.jp ドメインのみ許可している。
3. 投稿フォームUI: 書籍検索パネル
投稿フォームのツールバーに「読んでいる本・漫画を追加」ボタンを追加した。
ボタンをクリックするとフォームの下部からスライドアップで書籍検索パネルが開く。検索ワードを入力してEnterキーを押すかボタンをクリックすると、バックエンドAPIに非同期でリクエストを送り、結果を書影サムネイル付きリストで表示する。
書籍を選択するとパネルが閉じ、フォームの下に書影付きのプレビューカードが表示される。プレビューカードの「×」ボタンで選択を解除できる。
編集フォームにも同様の「書籍を変更 / 書籍情報を削除」セクションを追加した。
4. タイムライン表示: 引用カード
投稿に書籍情報が保存されている場合、つぶやき本文の下に引用カードを表示する。
カードのレイアウトは左に書影、右にタイトル・著者名・「Amazon」「楽天」リンクボタンの2カラム構成。デザインはゲーム情報カードやアニメ情報カードと同じ「四隅コーナーブラケット」スタイルで統一した。
レスポンシブ対応として、モバイルではパディングを少し狭くして書影を小さくしている。
5. 書籍フィルタ
ナビゲーションメニューに「book」フィルタタブを追加した。タブをクリックすると書籍情報付きの投稿だけに絞り込んで表示する。
サーバーサイドで書籍情報付き投稿の総件数を事前に取得しておき、無限スクロールが全件読み込み完了したタイミングを正確に判定する(既存のゲーム・写真フィルタと同じパターン)。
セキュリティ設計のポイント
- 検索APIは認証済みセッションを持つリクエストのみ処理する
- フロントエンドから受け取る全データをバックエンドで再バリデーションする
- URLフィールドはホワイトリスト方式でドメインを制限する(画像URL・楽天URL・Amazon URL)
- ISBNは数字のみに正規化して保存する
- DOM要素への書き込みはすべて
textContentまたは安全なDOM APIを使い、innerHTML は使用しない
今後の課題
- 書籍情報の検索履歴(localStorage)対応は未実装。ゲーム情報のように最近検索した書籍の履歴表示を追加するとより便利になる
- マンガなど縦長の書影と横長書影が混在する場合のレイアウト調整
楽天ブックスAPI 仕様変更(2026年2月)への対応記録
実装後、書籍検索を試みたところ「書籍情報の取得に失敗しました」というエラーが出て動作しなかった。原因の調査と修正に時間がかかったため、同様のトラブルに備えて記録しておく。
何が変わったのか
楽天ウェブサービスは2026年2月にAPIのエンドポイントを変更した。
| 項目 | 旧仕様(〜2026年1月) | 新仕様(2026年2月〜) |
|---|---|---|
| ドメイン | app.rakuten.co.jp |
openapi.rakuten.co.jp |
| 必須パラメータ | applicationId のみ |
applicationId + accessKey |
Origin / Referer ヘッダー |
不要 | 必須 |
| 許可ドメイン設定 | 不要 | 楽天デベロッパーダッシュボードで要登録 |
旧URLは2026年5月に廃止予定とのこと。
最終的に必要だった設定
以下の4点をすべて満たして初めて動作した。
1. エンドポイントURLの変更
https://openapi.rakuten.co.jp/services/api/BooksBook/Search/20170404
2. accessKey パラメータの追加
楽天デベロッパーダッシュボード(https://webservice.rakuten.co.jp/app/list)でアプリの詳細を開くと「アクセスキー」が表示される。これを RAKUTEN_ACCESS_KEY 環境変数に設定し、APIリクエストのクエリパラメータに accessKey として付加する。
3. Origin と Referer ヘッダーの付与
サーバーサイドからのfetchリクエストに以下のヘッダーを追加する。
Origin: https://(デプロイ先ドメイン)
Referer: https://(デプロイ先ドメイン)/
このヘッダーがないと 403 REQUEST_CONTEXT_BODY_HTTP_REFERRER_MISSING が返る。
4. 楽天デベロッパーダッシュボードでの許可ドメイン登録
アプリ設定の「許可するWebサイトのURL」にデプロイ先のドメイン(例: https://example.pages.dev)を登録する必要がある。この設定が抜けていると、正しい認証情報を付けても 403 が返り続ける。
環境変数のまとめ
RAKUTEN_APP_ID=(楽天アプリケーションID)
RAKUTEN_ACCESS_KEY=(楽天アクセスキー)
両方とも楽天デベロッパーダッシュボードのアプリ詳細画面から取得できる。コピー時に末尾に空白が入りやすいので .trim() 処理を入れておくとよい。