ニフクラ mobile backend(mBaaS)お役立ちブログ

スマホアプリ開発にニフクラ mobile backend(mBaaS)。アプリ開発に役立つ情報をおとどけ!

Web APIのレスポンスをデータストアにキャッシュする

f:id:mbaasdevrel:20171212211252p:plain

今のアプリはネットワークサービスありきで作られているものが多いです。mBaaSもそうですが、HTTP経由でWeb APIを呼び出してデータを取得したり、更新したりします。そうしたWeb APIを使う際にはそれぞれ決まった手順に沿って進める必要があります。

Web APIの呼び出しをクライアントで行っていると、コーディング量が増えたりして可読性が下がったり、メンテナンスが大変になります。そして何より、Web APIのキャッシュをしたいと思うとローカルのメモリに残しておくくらいしかありません。そこで今回はWeb APIへのアクセスをスクリプトで行い、キャッシュする方法を紹介します。

スクリプトの概要

今回はWeb APIの代表例として、RSSフィードをデータストアにキャッシュしてみます。まず全体のスクリプトは次のようになります。

// 1. ライブラリの読み込み

// 2. 定数を定義

// 3. NCMBの準備

// 4. メイン処理
module.exports = async (req, res) => {
  if (!req.query.url) {
  } else {
    res.json({});
  }
  // 5. キャッシュの検索
  
  // 6. フィードを取得
  
  // 7. フィールの中の記事を検索&登録
  
  // 8. フィードを更新
}

1. ライブラリの読み込み

まず必要なライブラリ、ネットワークアクセスのための superagent と NCMBを読み込みます。

// 1. ライブラリの読み込み
const request = require('superagent');
const NCMB = require('ncmb');

2. 定数の定義

定数を定義します。NCMBを扱うためのアプリケーションキーとクライアントキーです。

// 2. 定数を定義
const applicationKey = 'b34...01e';
const clientKey = '489...462';

3. NCMBの準備

NCMBで使うフィードクラス、記事クラスを準備します。

const ncmb = new NCMB(applicationKey, clientKey);
const Feed = ncmb.DataStore('Feed');
const Entry = ncmb.DataStore('Entry');

4. メイン処理

メイン処理では、パラメータとしてGETが送られているかどうかをチェックしています。あれば処理を続けます。

// メイン処理
module.exports = async (req, res) => {
  if (!req.query.url) {
  } else {
    res.json({});
  }
  // 5以降の処理
}

5. キャッシュの検索

フィードは1時間ごとにチェックする前提としています。すでに登録されているかどうかをチェックしています。

// 5. キャッシュの検索
const date = new Date;
date.setHours(date.getHours() - 1); // 1時間前に設定
let feed = await Feed.equalTo('url', req.query.url).fetch();
if (Object.keys(feed).length > 0) {
  if (new Date(feed.fetchDate.iso) > date) {
    res.json({
      objectId: feed.objectId,
      nextFetchDate: new Date(feed.fetchDate.iso)
    });
    return;
  }
} else {
  feed = new Feed;
  feed.set('url', req.query.url);
}

6. フィードを取得

フィードはRSS to JSON Converter onlineを使ってRSSをJSON化しています。そのデータを取得して、フィードクラスに適用しています。

// 6. フィードを取得
const url = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURI(req.query.url)}`;
response = await request
  .get(url)
  .send();
const json = await response.body;
for (const key in json.feed) {
  if (key !== 'items' && key !== 'url') {
    feed.set(key, json[key]);
  }
}

7. フィールの中の記事を検索&登録

記事を処理します。この時、リレーションを使ってフィードとの紐付けを行っています。また、guidを使ってユニークチェックを行っています。

// 7. フィールの中の記事を検索&登録
const entries = [];
const relation = new ncmb.Relation();
for (const item of json.items) {
  let entry = await Entry.equalTo('guid', item.guid).fetch();
  if (Object.keys(entry).length > 0) {
    relation.add(entry);
  }
  entry = new Entry;
  for (const key in item) {
    if (['created', 'updated'].indexOf(key) > -1) {
      entry.set(key, new Date(item[key]));
    } else {
      entry.set(key, item[key]);
    }
  }
  relation.add(entry);
}

8. フィードを更新

最後に記事とフィードを結びつけて保存します。これで処理が完了します。

// フィードを更新
feed.set('entries', relation);
feed.set('fetchDate', new Date);
const method = feed.objectId ? 'update' : 'save';
try {
  await feed[method]();
  res.json({
    objectId: feed.objectId,
    nextFetchDate: feed.fetchDate
  });
} catch (e) {
  res.json(e);
}

コードについて

コードはNCMBMania/webapi-cache-script: スクリプト機能でWeb APIのレスポンスをキャッシュしますにて公開しています。script.jsがスクリプトの内容になります。

使い方

このスクリプトはクライアントアプリからの実行は想定していません。ニフクラのタイマー機能やGoogleスプレッドシートのタイマーを使って定期実行するのを想定しています。そうして、クライアントアプリからは常にフィードクラス、記事クラスを呼び出して使います。

こうすればRSSフィードや他のWeb APIであってもmBaaSのデータストアと同じように利用できます。

まとめ

Web APIがJSONだった場合、JSONのまま扱うのは不便なことも多いです。mBaaSに一旦データを保存しておけば、再利用も簡単になるのでお勧めです。

中津川 篤司

中津川 篤司

NCMBエヴァンジェリスト。プログラマ、エンジニアとしていくつかの企業で働き、28歳のときに独立。 2004年、まだ情報が少なかったオープンソースソフトの技術ブログ「MOONGIFT」を開設し、毎日情報を発信している。2013年に法人化、ビジネスとエンジニアを結ぶDXエージェンシー「DevRel」活動をスタート。