Ribbit.work

TypeScriptで楽天ブックス書籍検索APIを使う📚

TypeScriptJavaScript

last modified date2021-9-14

publish date2021-9-13

こんにちはリビットです。

最近ツイッターもフェイスブックも、APIを使用するための審査が厳しくなっているようですね。

申請にあたって色んな作文を書かないといけませんが、楽天APIは幸い、楽天のアカウントさえ持っていれば利用することができます。

今回はその中でも、楽天ブックス書籍検索APIを使って、目的の本を取得する処理を作成したいと思います。

ちなみにこのサイトは静的サイトジェネレータを使って作成していますが、今回ご紹介するコードを使って、ビルド時に各記事に関連する本を自動取得する処理をいれています。

確認したい方は記事の一番下までどうぞ。

型定義

今回紹介するのは、他のAPIも含めてすべて1つの名前空間に収めてしまう方法を使っていますが。ご自由に分解して使ってください。

/** 複数のうち最低1つが必須となっているパラメータ用 */
type RequireOne<T, K extends keyof T = keyof T> = K extends keyof T ? PartialRequire<T, K> : never;
type PartialRequire<O, K extends keyof O> = {
  [P in K]-?: O[P];
} & O;

export namespace rakuten {
  export namespace api {
    /** ソートに指定できる文字列 */
    export type Sorting =
      | "+reviewCount"
      | "-reviewCount"
      | "+reviewAverage"
      | "-reviewAverage"
      | "+itemPrice"
      | "-itemPrice"
      | "+updateTimestamp"
      | "-updateTimestamp"
      | "standard";

    export namespace books {
      type Auth = {
        applicationId: string;
        affiliateId?: string;
      };

      type Options = {
        /** デフォルト: json */
        format?: "json" | "xml";
        callback?: string;
        elements?: (keyof Book)[];
        /** デフォルト: 1 */
        formatVersion?: 1 | 2;
        title?: string;

        /** 1から30まで。デフォルトは30 */
        hits?: number;
        page?: number;
        availability?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
        outOfStockFlag?: 0 | 1;
        chirayomiFlag?: 0 | 1;
        sort?: Sorting;
        limitedFlag?: 0 | 1;
        carrier?: number;
        genreInformationFlag?: 0 | 1;
      };

      type EitherRequired = RequireOne<{
        title?: string;
        author?: string;
        publisherName?: string;
        size?: number;
        isbn?: string;
        booksGenreId?: string;
      }>;

      export type Request = Auth & Options & EitherRequired;

      export type RequestWithoutAuth = Options & EitherRequired;

      /** フォーマットバージョン2 */
      export type Book = {
        limitedFlag: 0 | 1;
        authorKana: string;
        author: string;
        subTitle: string;
        seriesNameKana: string;
        title: string;
        subTitleKana: string;
        itemCaption: string;
        publisherName: string;
        listPrice: 0;
        isbn: string;
        largeImageUrl: string;
        mediumImageUrl: string;
        titleKana: string;
        availability: "1";
        postageFlag: 2;
        salesDate: string;
        contents: string;
        smallImageUrl: string;
        discountPrice: 0;
        itemPrice: number;
        size: string;
        booksGenreId: string;
        affiliateUrl: string;
        seriesName: string;
        reviewCount: number;
        reviewAverage: string;
        discountRate: 0;
        chirayomiUrl: string;
        itemUrl: string;
      };

      export type Response = { Items: Book[] };
    }
  }
}

APIにはformatVersionというパラメータがあり、これを変更することで返ってくるJSONの構造が変わります。

バージョンには1と2がありますが、2の方が使いやすいので、今回は2を必ず指定することを前提として定義してます。

基本的には公式ドキュメントの通り型を定義しただけですが、TypeScriptの恩恵を受けやすくするため、よく利用するelementsパラメータだけは型を変更しているので注意してください。

APIのエンドポイントに負担をかけないためにも、仕様が固まったらelementsパラメータは最小限で指定するようにしましょう。

APIクラス

import axios from 'axios';
import { RakutenAPIClient } from '.';
import { rakuten } from './types';

const DOMAIN = 'https://app.rakuten.co.jp/';

export class RakutenBooksClient {
  public static END_POINT = 'services/api/BooksBook/Search/20170404';

  public async get(request: rakuten.api.books.Request) {
    const url = this.getUrl(request);
    const response = await axios.get<rakuten.api.books.Response>(url);
    return response;
  }

  private getUrl(request: rakuten.api.books.Request) {
    let url = RakutenAPIClient.DOMAIN + RakutenBooksClient.END_POINT;
    url += `?applicationId=${request.applicationId}`;
    url += `&affiliateId=${request.affiliateId}`;
    url += `&formatVersion=2`;

    if (request.callback) {
      url += `&callback=${request.callback}`;
    }
    if (request.elements) {
      url += `&elements=${request.elements.join(',')}`;
    }

    if (request.title) {
      url += `&title=${encodeURIComponent(request.title)}`;
    }
    if (request.author) {
      url += `&author=${encodeURIComponent(request.author)}`;
    }
    if (request.publisherName) {
      url += `&publisherName=${encodeURIComponent(request.publisherName)}`;
    }
    if (request.size) {
      url += `&size=${request.size}`;
    }
    if (request.isbn) {
      url += `&isbn=${request.isbn}`;
    }
    if (request.booksGenreId) {
      url += `&booksGenreId=${request.booksGenreId}`;
    }
    if (request.hits) {
      url += `&hits=${request.hits}`;
    }
    if (request.page) {
      url += `&page=${request.page}`;
    }
    if (request.availability) {
      url += `&availability=${request.availability}`;
    }
    if (request.outOfStockFlag) {
      url += `&outOfStockFlag=${request.outOfStockFlag}`;
    }
    if (request.chirayomiFlag) {
      url += `&chirayomiFlag=${request.chirayomiFlag}`;
    }
    if (request.sort) {
      url += `&sort=${request.sort}`;
    }
    if (request.limitedFlag) {
      url += `&limitedFlag=${request.limitedFlag}`;
    }
    if (request.carrier) {
      url += `&carrier=${request.carrier}`;
    }
    if (request.genreInformationFlag) {
      url += `&genreInformationFlag=${request.genreInformationFlag}`;
    }

    return url;
  }
}

ほぼ条件式ですね。

今回はメンバー変数が1つもありませんが、認証情報を予め指定しても良いと思います。

export class RakutenBooksClient {
  public static END_POINT = 'services/api/BooksBook/Search/20170404';

  private readonly _applicationId: string;
  private readonly _applicationSecret: string;
  private readonly _affiliateId: string;

  public constructor(applicationId: string, applicationSecret: string, affiliateId: string) {
    this._applicationId = applicationId;
    this._applicationSecret = applicationSecret;
    this._affiliateId = affiliateId;
  }

  //...

使い方の例

import { RakutenBooksClient } from "./rakuten-books-client";

(async () => {
  const client = new RakutenBooksClient();

  const response = await client.get({
    applicationId: "____ここに取得したアプリIDを設定____",
    title: "こんにちは",
    hits: 4,
    availability: 1,
    elements: ["title", "author", "largeImageUrl", "itemPrice", "affiliateUrl"],
  });

  console.log(response);
})();

この記事を読んだ方におすすめの記事

KintoneからChatworkのユーザ一覧を取得する

KintoneからChatworkのユーザ一覧を取得する

JavaScriptKintone
2021-9-15
【kintone】Webフォントを適用する

【kintone】Webフォントを適用する

KintoneJavaScript
2021-9-8
KintoneからChatworkにメッセージを送る

KintoneからChatworkにメッセージを送る

JavaScript
2021-9-6
アクセス権限のないアプリにJavaScriptからだけアクセスを許可する方法

アクセス権限のないアプリにJavaScriptからだけアクセスを許可する方法

KintoneJavaScript
2021-9-3
【Kintone】フィールドの入力制限・可否を変更する

【Kintone】フィールドの入力制限・可否を変更する

KintoneJavaScript
2021-9-2

最新の記事

KintoneからChatworkのユーザ一覧を取得する

KintoneからChatworkのユーザ一覧を取得する

JavaScriptKintone
2021-9-15
【kintone】Webフォントを適用する

【kintone】Webフォントを適用する

KintoneJavaScript
2021-9-8
KintoneからChatworkにメッセージを送る

KintoneからChatworkにメッセージを送る

JavaScript
2021-9-6
Excel VBAの高速化はこれだけでOK!コピペで使えるコードを紹介

Excel VBAの高速化はこれだけでOK!コピペで使えるコードを紹介

VBA
2021-9-3
アクセス権限のないアプリにJavaScriptからだけアクセスを許可する方法

アクセス権限のないアプリにJavaScriptからだけアクセスを許可する方法

KintoneJavaScript
2021-9-3