読者です 読者をやめる 読者になる 読者になる

YQLを利用してRSSを取得する

はてブのホットエントリーをJSON形式で取得してReduxを利用したクライアントで表示してみます。

完成品

f:id:ktdk:20160802104044g:plain

(左に寄っちゃってますが...)

YQL (Yahoo! Query Console)

CORSによるクロスドメイン制限があるため、Ajaxで直接RSSを取得することはできません。
PHP等のサーバーサイドでRSSを取得してクライアントに表示する必要があります。

今回はサーバーの実装をしないので、YQLを利用します。

developer.yahoo.com

こんな感じでSQLのようにクエリを実行すると結果が返ってきます。

f:id:ktdk:20160802102122p:plain

最下部にあるTHE REST QUERYcurlで叩いてみると、RSSJSON形式で取得できます。

$ curl -X GET "https://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20rss%20WHERE%20url%20%3D%20'http%3A%2F%2Fb.hatena.ne.jp%2Fhotentry.rss'&format=json&diagnostics=true&callback="

{"query":{"count":30,"created":"2016-08-02T01:23:13Z","lang":"en-US","diagnostics":{"publiclyCallable":"true","url":{"execution-start-time":"1","execution-stop-time":"452","execution-time":"451","content":"http://b.hatena.ne.jp/hotentry.rss"},"user-time":"454","service-time":"451","build-version":"0.2.39"},"results":{"item":[{"about":"http://www.okinawatimes.co.jp/articles/-/55298","title":"スク水揚げ 奥武島で今年も大漁 | 沖縄タイムス+プラス ニュース","link":"http://www.okinawatimes.co.jp/articles/-/55298","description":"【南城】南城市の奥武島でスク漁が始まり、島の海人が
...

Fetch API

RSSを取得するだけなのでsuperagent等のモジュールではなくFetch APIを利用してみます。

Fetch Standard (日本語訳)

redux-actions、redux-promiseと組み合わせる

fetch()はPromiseを返すのでredux-promiseにそのまま渡します。
then()でreturnした結果がreducerのnextのaction.payloadに渡されます。

// api/bookmarks.js
import querystring from 'querystring'
import { getRssUrl } from '../constants/ApiCategory'

const YQL_URL = "https://query.yahooapis.com/v1/public/yql"

const defaultParams = {
  format: 'json',
  diagnotistics: true
}

export default {
  fetchBookmarks: (category) => {
    const sql = `SELECT * FROM rss WHERE url = '${getRssUrl(category)}'`
    const data = Object.assign({}, defaultParams, { q: sql })
    const url = `${YQL_URL}?${querystring.stringify(data)}`

    // Promiseを返す
    return fetch(url)
      .then((res) => {
        return res.json()
      })
      .catch((err) => {
        console.log(err)
      })
  }
}
// actions/api.js
import { createAction } from 'redux-actions'
import * as types from '../constants/ApiActions'
import api from '../api/bookmarks'

export const fetchBookmarks =
  createAction(types.FETCH_BOOKMARKS, api.fetchBookmarks)
// reducers/bookmarks.js
const bookmarks = handleActions({
  [types.FETCH_BOOKMARKS]: {
    next: (state, action) => {
      let items = action.payload.query.results.item
      return items.map(b => bookmark(b, action))
    },

    throw: (state, action) => {
      return []
    }
  }
}, [])

今回は1つのstateを全タブで共有しているためクリック毎にrequestが飛んでしまいますが、
カテゴリ(タブ)ごとにstate持たせて管理するとrequest回数も減らせてサクサク動きますかね。

ソースコード

github.com