Graphqlのデータを表示するためのDecorator

SPAのアプリケーションを実装する時にREST APIでなくGraphqlを利用することが増えてきました。

Graphqlを利用することで各ページに必要なデータのみを取得できるのは便利ですね。

表示するデータはGraphqlのデータフォーマットだけではない

たとえば下記のようなデータを返すqueryがあるとします。

{
  data: {
    user: {
      firstName: 'Koji',
      lastName: 'Murakami',
      avatar: {
        file: {
          url: 'path to avatar url'
          name: 'Koji Murakami'
        }
      }
    }
  }
}

データとしてはとても自然ですがViewで表示するときにデータの加工が必要になることもあります。

たとえばfirstNameとlastNameをあわせてfullNameを表示したい場合はViewのほうで下記のような値をテンプレートにいれることになります。

`${user.firstName} ${user.lastName}` // Koji Murakami

1箇所だけならそれでも良いですが複数の箇所で使うことを想定するとfullNameの値を得る処理は一つにしたくなります。

DecoratorでViewに必要なデータを返す

Viewで必要なデータを返すために下記のような関数を用意するのが便利です。

// userDecorator.js

export default(user) => {
  return {
    get fullName() {
      const { firstName, lastName } = user
      
      return firstName && lastName ? `${firstName} ${lastName}`
    },
    get avatarUrl() {
      return user.avatar.file ? user.avatar.file.url : '/images/default-avatar.png'
    }
  }
}

今回はReactを例にしますがVueでもAngularでもやることに変わりはありません。Graphqlで取得したデータを用意した関数に渡すことでViewにロジックを書くことなくテンプレートで使うことが可能になります。

import userDecorator from 'path/to/userDecorator'

const User = (props) => {
  const user = userDecorator(propd.data.user)
  
  return (
    <div>名前:{user.fullName}</div>
  )
}

Graphqlではモデルはしっくりこない

REST APIのころはViewに必要なデータはUserモデルっぽいのを作って表示してみたりしたこともありました。

class User {
  constructor(data) {
    this.firstName = data.firstName
    this.lastName = data.lastName
  }
  
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}

こんな感じのクラスを用意すれば同じことができます。

ですがGaraphqlを使うとデータ初期化の時点でViewで必要なデータしか取得しないためクラスでfirstNameはあるけどlastNameがない、というような状態になるのが気持ち悪くなってしまいました。

Decoratorはプロジェクトの規模によって判断する

今回紹介した方法はViewと表示に必要な加工処理を切り分けることができるのでコンポーネントをシンプルに保つことができます。

その一方で処理を切り出すことになるため冗長になる可能性も含んでいます。

たとえばVueではcomputedというtemplateで使うのに加工するための仕組みもあり、どちらを使ったほうがよいかはケースバイケースですね。

スケールしそうなプロジェクトや同じ記述をテンプレートに書きたくないという場面では今回の方法を使ってみると良いかなと思います!

コウジ
Vue.jsが好きなフリーランスのフロントエンドエンジニアです。 フロントのHTML・CSS・JavaScriptの設計実装をメインに、RubyとNodeでサーバーサイドやってたりもします。