GraphQL サーバーのモックを作る

GraphQL の API と通信するアプリケーションを作る際には開発時に API サーバーのモックが必要になると思います。API 側ではまだ実現できていない Query や Mutation を呼び出すことがあるからです。そこでどのように作ればいいか紹介します。

Apollo Server を利用する

www.apollographql.com

実際にこれで API サーバーを作ってるかどうかは関係なく、フロント用のモックとして気軽に利用できます。

以下は簡単なサンプルです。ほぼ公式通りですが…。

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

const resolvers = {
  Query: {
    books: () => [
      {
        title: 'Book1',
        author: 'John',
      },
      {
        title: 'Book2',
        author: 'Mike',
      },
    ],
  },
};

new ApolloServer({
  typeDefs,
  resolvers
})
  .listen()
  .then(({ url }) => {
    console.log(`🚀  Server ready at ${url}`);
  });

これを実行するとサーバーが立ち上がります。(ポート番号は変更できます)

$ node mock/index.js
🚀  Server ready at http://localhost:4000/

サーバーにアクセスするとプレイグラウンドが立ち上がり、実際にクエリが試せます。

実際にはスキーマやリゾルバは別ファイルにして管理したほうがいいでしょう。スキーマについては別ファイルで管理してフロントとAPI側で共通で使用することが多いと思うのでそれをインポートして使えます。

f:id:kaz_shu:20200425215735p:plain

実際に Schema や Query を定義した後にダミーデータを作成してフロントとの連携のテストに使うといいと思います。難点は実際にデータの登録等はできないので Mutation の結果をクエリに反映できません。 Query や Mutation のロジックに log を仕込んで正しい API が叩かれているかを確かめる必要があると思います。

Prisma を利用する

以下の記事が詳しいです。

qiita.com

モック用というわけではないですが、試すことはできます。 スキーマを定義すると裏では docker で RDB(MySQLなど選択可能) が動いてデータを永続化できます。簡単なスキーマでは試しましたが、複雑なスキーマや複数人で開発する際に向いていないかなと判断し、上記の Apollo Server を使用しています。独自のスキーマ定義が必要となるのでスキーマAPI 側と共通にしたい場合は向きません。

www.prisma.io

React アプリケーションの Apollo Client を使ったリビジョンアップ方法

React のアプリケーションを修正してリリースしてもユーザーの端末ではリロードしないと新しいスクリプトを読み込んでくれなくて古いアプリケーションが動いたままになることがあります。検索して以下のような情報がありました。

qiita.com

リビジョンIDをアプリケーション内とファイルで持っておいて、ファイルを読み込んで差異があったらリロードをするというものです。GraphQL のアプリケーションだったので今回はこれを参考にし、Apollo Client + Apollo Link Rest を使って実現します。

前提

  • Apollo Client + GraphQL のアプリケーションに追加する
  • TypeScript で実装している(コードの例は TypeScript です)

Apollo Link Rest をインストール

Apollo Link Rest は Apollo Client で REST API を叩けるモジュールです。GraphQL と同じような Query を作り、同じように処理を書くことができます。公式も参考に。

https://www.apollographql.com/docs/link/links/rest/

モジュールをインストールします。GraphQL 関連はインストール済みの前提で新たに必要なもののみ記載しています。

$ yarn add apollo-link-rest@0.7.3 graphql-anywhere qs

バージョンを 0.7.3 に固定しているのは 2020/4/23 時点で latest を取得すると apollo-client@3 以降を要求するバージョンになるためです。

クライアントを定義

ほぼ公式そのままです。

const restLink = new RestLink({
  uri: 'https://www.example.com/',
});

export const client = new ApolloClient({
  link: restLink,
  cache: new InMemoryCache(),
});

ここで作成したクライアントを使用して以下のクエリを叩きます。 GraphQL 用の client と被らないようにしましょう。

クエリを定義

type Revision = {
  revision: {
    value: string;
  }
};

const Query = gql`
query findRevision {
  revision @rest(type: "Revision", path: "revision.json") {
    value
  }
};
`

ここでは Revision という型を定義し、Query の戻りの型として設定しています。

クエリ処理

次に実際にクエリを叩くところです。 Hooks を利用しています。

const { loading, data } = useQuery<Revision>();

if (loading || !data) return null;

if (currentVersion !== data.revision.value) {
  window.location.reload(true);
}

このように、 GraphQL のクエリと同じ様に Rest を叩くことができます。 currentVersion はビルド時にリビジョンIDを生成して定義しておくといいでしょう。 例えばビルド時にタイムスタンプを生成して process.env.RevisionID とかにマッピングしておくとかがあります。 webpack を使用している場合は webpack.DefinePlugin で実現できます。 revision.json についても同じタイムスタンプにする必要があるので同じくビルド時にその値でファイルを作っておくといいと思います。

アプリケーションに組み込む

このままだとこれを組み込んだコンポーネントが読まれたときに1回実行されるだけになります。 react-router などを利用している場合はルーティングされるたびにマウントされる場所に置いておけばいいかもしれませんが頻度が高いかもしれません。それを解決する方法としては任意の間隔でポーリングする方法がありますが、 useQuery ではポーリングを設定できるのでそれを利用するのも1つです。

useQuery<Revision>({
  pollInterval: 60000, // ミリ秒
  fetchPolicy: 'network-only',
});

以上のようにすると60秒毎にクエリが発行されます。詳しくは公式を参考にしてください。

https://www.apollographql.com/docs/react/data/queries/

まとめ

axios などのモジュールを使わず useQuery で GraphQL を使っているかのように実装できました。

リビジョンチェックのサンプルです

json ファイル

{ "value": "20200401112233" }

コンポーネント

import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag';

type Revision = {
  revision: {
    value: string;
  };
};

const Query = gql`
  query findRevision {
    revision @rest(type: "Revision", path: "revision.json") {
      value
    }
  }
`;

export const Reloader = () => {
  const { loading, data } = useQuery<Revision>(Query, {
    pollInterval: 60000,
    fetchPolicy: 'network-only',
  });

  if (loading || !data) return null;

  const currentVersion = process.env.RevisionID;

  if (currentVersion !== data.revision.value) {
    window.location.reload(true);
  }

  return null;
};

ターミナルの操作を快適にする2つのツール

ターミナルの操作で普段からよく使っている2つのツールを紹介します。

環境

1. peco

github.com

Simplistic interactive filtering tool ということでフィルタリングツールです。 主にコマンドの履歴を探すのに使っています。 インクリメンタルサーチができるので目的のコマンドを大変見つけやすいです。 peco コマンドはパイプで渡すと渡されたものを検索できるようになります

例)

$ ps aux | peco

インストール

Homebrew で簡単にインストールできます。 go で書かれているのであわせてインストールされます。

$ brew install peco

これで peco のコマンドは使えるようになりました。 しかし、コマンドの履歴でシームレスに使えるようにするために、シェルの設定を追加します。

zsh の場合

function peco-select-history() {
   local tac
   if which tac > /dev/null; then
       tac="tac"
   else
       tac="tail -r"
   fi
   BUFFER=$(\history -n 1 | \
       eval $tac | \
       peco --query "$LBUFFER")
   CURSOR=$#BUFFER
   zle clear-screen
}
zle -N peco-select-history
bindkey '^r' peco-select-history

fish の場合

oh-my-fish, fisherman 等で plugin を入れておくと楽です。

# oh-my-fish
$ omf install peco
# fisherman
$ fisher oh-my-fish/plugin-peco

以下をシェルの設定に書きます。

function fish_user_key_bindings
  bind \cr 'peco_select_history (commandline -b)'
end

使用方法

ctrl + r でコマンド履歴を検索できます。

2. z

github.com

ディレクトリを簡単に移動できるツールで、他には autojump などがあります。 以前は autojump を使っていましたが、z に移行しました。

インストール

$ brew install z

シェルの設定を追加します。

zsh の場合

. /usr/local/etc/profile.d/z.sh

fish の場合

oh-my-fish, fisherman 等で plugin を入れておくと楽です。

# oh-my-fish
$ omf install z
# fisherman
$ fisher z

使用方法

$ z hoge
$ zo hoge

zhoge というワードで一番マッチしたディレクトリに移動します。 zohoge というワードで一番マッチしたディレクトリを finder 等で開きます。

autojump は標準ではコマンドが j なので、 j に慣れている場合は割り当てちゃいましょう。

El Capitan で Ruby, gem のインストールではまったこと

Ruby や gem はしばらくインストールをしていなかったが、 El Capitan にアップデート後に一度 rbenv でインストールした Ruby を削除したので、再インストールした。また、gem もインストールしたが、いろいろとエラーが出て悩んでいた…。いろいろ調べて解決したがそれをまとめておく。

rbenv で Ruby がインストールできない

以前は以下のコマンドでインストールしていた

$ RUBY_CONFIGURE_OPTS="--with-readline-dir=`brew --prefix readline` --with-openssl-dir=`brew --prefix openssl`" rbenv install 2.2.3

しかし、これでビルドが止まってしまう。

checking whether CFLAGS is valid... no

以下を足すことでで解決

CC=/usr/bin/gcc

pg がインストールできない

gem の pg もインストールができなくなった。

Can't find the PostgreSQL client library (libpq)

以下を足すことで解決

ARCHFLAGS="-arch x86_64"

bundle install する際も頭につけておく

$ ARCHFLAGS="-arch x86_64" bundle install

gcc とかいろいろアップデートして環境が変わったからかもしれない。

Rails Girls Tokyo 5th でコーチしてきました

エンジニア人生がだいぶ変わった Ruby/Rails について少しでも貢献できればとコーチで参加してきました。

プログラミング初心者がほとんどだったのでできるだけ難しい言葉は使わず Ruby, Rails, Web について説明しました。 heroku へのデプロイまで何とかうまく行ったので満足されていたと思います。

Rails は半年くらい触ってなかったのでキャッチアップしたり、 Windows マシン担当になったので Windows で セットアップしてみたり、会社の人に練習台になってもらったりと準備は万端でした(笑

次回も機会があれば…。

そういえば、転職して2年が経った

1年間とおしてどうだったのだろう…。

ざっと

一応、プロダクトの責任者の1人になっていた。コーディングだけでなく、プロダクトの成長・方向性を考える1人になっていた。また、メンバーをまとめたり、教育したりといろいろ経験出来た。失敗も多かったけど得られたモノのほうが大きかったと思う。ビジネス側と開発側の折衝って難しいなあと改めて感じられた。

エンジニアとしてやってきたことを振り返る

  • ScalaHadoop の job を書いた。Scalding を使って。
  • ApacheMySQL 等ミドル系に触れることが多かったのでいろいろ身についた。
  • SVN -> github にソースを移したり、ビルド環境を整えたりと環境構築・以降などをバシバシ実施した
  • 古い画面システムを Rails でリプレースしようと決心した。
  • 障害対応・予防についていろいろ経験出来た。

この1年はどうするか

  • Scala 熱を増やす?今いる現場は Scala 推し。積極的に導入していきたい。簡単な集計処理は Java とかで書くよりスマートで、可読性もいいのでバグが起こりにくいと思う。
  • やっぱりテスト。チームにテスト文化を根付かせる。
  • 新しい言語を試す。何にしよう。Haskell をとりあえずまじめにやってみるか。
  • Spark?
  • スマートフォンアプリを作るか。
  • gem を1個は公開してみよう。

限られた時間のなかで無駄を少し削り、勉強やモノを作る時間を増やそう。 広く浅くになってしまうのなら、少数を深堀りした方がいいとはお思うけど、尊敬できるすごい人たちってやっぱり幅広く深い知識を持っている気がする。そうなりたい。

転職して1年

転職して丸1年が立ちました。

ちょっと酔っていますが、超簡単な振り返りをします。

前職は…

現職は3社目です。前職は業務委託・派遣でいろいろなところに常駐するSESの会社でした。いろいろな現場(主に金融系)で経験を積んで来ましたが、会社のために何かしたかというとほとんど何もしてませんでした。職場は常駐先なので同僚と顔を合わせるのは年に数回の会議だけでした。

これじゃあ、会社に属している意味が…と思ったり、常駐先で商品等を開発するのと、自社サービスとではいろいろと意識が違うので自社サービスをやりたいと思い転職に踏み切りました。もちろん、前職でも作ろうと試みましたが、社員の意識・経験不足等で簡単なWebサービスを始めましたが、すぐに放置されました。

画面、サーバ、バッチ

入社して、最初はフロントの画面を担当しました。いろいろアレでしたがなんとか踏ん張り、その後サーバサイドも手をつけて、バッチ処理も触ったりといろいろやらせてもらっています。 Ruby, Java, Scala, Hadoop etc... まだまだ勉強不足でいろいろ助けてもらっていますが、1年前に比べると確かに成長できているなと実感出来ます。

特に直近では Ruby ばかりだったので Java はブランクがあり、Ruby に慣れているとまどろっこしく感じられますが、IDE である IntelliJ の優秀さを実感したり、興味はあったけどあまり触れてなかった Scala を本格的に使ったりといろいろな技術に挑戦できる環境はすごくいいです。

仲間

優秀なエンジニアが揃っています。意識高い()です!私が入社したときは少なかったですが、今は当時の倍近くになり、活発に社内勉強会や自主的なもくもく会が行われています。勉強会で発表するときに、プレゼンの資料を作って発表するのは大変だなーと発表の経験をするというのはとてもいいことだと思いました。

エンジニアだけでなく、営業さんたちと同じフロアにいるのでいっしょにサービスを作っている感が素敵です。時にはぶつかり合うことももちろんありますが、仲が良いだけで慣れ合いになるのはダメなのでそこは遠慮せずにこれからもやって行きたいです。 エンジニアとは明らかに違う雰囲気を持っている営業さんたちはいろいろと参考になります…。

2年目は

きっちり目標を立ててまた半年後、1年後に振り返ったときにまた書けるようにしたいです。 Ruby, Scala をもっと強くし、テストをさらに意識してやって行きます。 またサービスが盛り上がるように積極的にコードを書くだけではなく活動したいと思います。

Ruby を始めてちょうど2年になりますが、それの振り返りは次の機会に…。