2020年6月17日水曜日

[GraphQL] TypeScript+VSCode+Apollo is Great DX.

https://dev.classmethod.jp/articles/apollo-good-dx/

シェアしました。

[GraphQL] TypeScript+VSCode+Apolloで最高のDXを手に入れよう

GraphQLを利用するWebアプリケーションの開発で、TypeScript + VSCode + Apollo のスタックで開発環境を構成すると、とても最高なDX(Developer Experience)が得られたので、紹介したいと思います。
どのくらい最高かを想像しやすくするため、従来のBADなDXの例を挙げてみますと...「APIのドキュメントに誤りがあり通信に失敗する」「APIの変更がドキュメントに反映されていなくてサーバサイドのエンジニアに問い合わせる」「APIのドキュメントを見ながらAPIリクエスト/レスポンスのオブジェクトを手作りで実装する」などなど、よくある話かと思います。従来のREST APIなどでは実装とドキュメントが分離されているので、そこを埋めるためのエンジニアの作業が必要です(そこをツールで埋めようとする試みは古くからありますが)。一方で、GraphQLは実装とスキーマ定義が一体なので、無理なくツールの力を借りやすくなっており、エンジニアの作業を省く(=DXを向上させる)ことができます。
前置きが長くなりましたが、このスタックで得られるDXは次のとおりです。
  • VSCode上でQueryやMutationの入力補完を使うことができる
  • QueryやMutationのレスポンスの型を自動生成できる

環境

  • TypeScript: 3.7.2
  • VSCode: 1.40.1
  • Apollo GraphQL(VSCode拡張): 1.12.1
  • apollo-tooling(CLI): 2.21.0

サンプルの説明

この記事では、実装のサンプルとしてswapi-graphqlからデータを取得して表示するプロジェクトを作成しましたのでそれを例に上げながら説明します。お手元の環境にgit cloneして、ぜひ実際に動作を体験してみてください。

QueryやMutationの入力補完

手順
  • VSCodeに拡張機能の Apollo GraphQL を追加します。
  • apollo.config.jsにGraphQLサーバの接続情報を設定します。
  • VSCodeのコマンドパレットで Apollo: Reload schema を実行します。
以上の設定を終えると、 gql タグで囲った内部で、入力補完が働くようになります。

レスポンスの型を自動生成

手順
  • apollo-toolingをインストールします。インストールせずにnpxを使っても良いです。
  • client:codegen コマンドを実行します。 apollo client:codegen --target typescript types
以上の手順を終えると、 gql タグで囲った内部に定義したQueryやMutationのレスポンスの型が自動生成されます。またEnumやInput Objectの型定義も自動生成されます。
./src/types/GetFilms.ts
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.

// ====================================================
// GraphQL query operation: GetFilms
// ====================================================

export interface GetFilms_allFilms_films {
  __typename: "Film";
  /**
   * The episode number of this film.
   */
  episodeID: number | null;
  /**
   * The title of this film.
   */
  title: string | null;
}

export interface GetFilms_allFilms {
  __typename: "FilmsConnection";
  /**
   * A list of all of the objects returned in the connection. This is a convenience
   * field provided for quickly exploring the API; rather than querying for
   * "{ edges { node } }" when no edge data is needed, this field can be be used
   * instead. Note that when clients like Relay need to fetch the "cursor" field on
   * the edge to enable efficient pagination, this shortcut cannot be used, and the
   * full "{ edges { node } }" version should be used instead.
   */
  films: (GetFilms_allFilms_films | null)[] | null;
}

export interface GetFilms {
  allFilms: GetFilms_allFilms | null;
}
これを useQuery の型引数として渡してやればOKです。
const GET_FILMS = gql`
  query GetFilms {
    allFilms {
      films {
        title
        episodeID
      }
    }
  }
`

const Contents: React.FC = () => {
  const { loading, error, data } = useQuery<GetFilms>(GET_FILMS)

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error :</div>
  if (!data) return <div>Error : data is undefined</div>

  return (
    <ul>
      {data.allFilms!.films!.map(film => (
        <li>
          episode {film!.episodeID}: {film!.title}
        </li>
      ))}
    </ul>
  )
}

おわりに

Apolloのエコシステムを利用することで、GraphQLを利用するクライアントアプリケーションの実装が省力化できることがおわかりいただけたかと思います。今回紹介したのと同じような内容がGraphQL Summit 2019のセッションにもありますので、ぜひこちらも御覧になってください。

[GraphQL] Get the best DX with TypeScript+VSCode+Apollo

When developing a web application that uses GraphQL, a very good DX (Developer Experience) was obtained by configuring the development environment with the stack of TypeScript + VSCode + Apollo, so I would like to introduce it.
To make it easier to imagine how best it is, let's take an example of a conventional BAD DX... "API documentation is incorrect and communication fails" "API changes are not reflected in the documentation I think it's a common story such as "contact the engineer on the server side" and "implement the API request/response object by hand while looking at the API documentation". Since the implementation and the document are separated in the conventional REST API etc., the work of the engineer is required to fill it (although there are old attempts to fill it with tools). On the other hand, in GraphQL, the implementation and the schema definition are integrated, so it is easy to borrow the power of tools, and the work of engineers can be omitted (= improve DX).
The foreword is longer, but the DX you get with this stack is:
  • Input completion of Query and Mutation can be used on VS Code
  • Query and Mutation response types can be automatically generated

environment

  • TypeScript: 3.7.2
  • VSCode: 1.40.1
  • Apollo GraphQL (VS Code extension): 1.12.1
  • apollo-tooling(CLI): 2.21.0

Sample description

In this article, I created a project that acquires data from swapi-graphql and displays it as an implementation sample, so I will explain it by taking it as an example. Please try git clone to your environment and experience it in action .

Input completion for Query and Mutation

procedure
  • Add Apollo GraphQL extension to VS Code .
  • Set connection information of GraphQL server in apollo.config.js.
  • Run in the VS Code command palette Apollo: Reload schema.
After completing the above settings gql, input completion will work inside the tag.

Automatically generate response type

procedure
  • Install apollo-tooling . You can use npx without installing it.
  • client:codegen Execute the command. apollo client:codegen --target typescript types
When the above steps are completed, gqlthe response type of Query or Mutation defined inside the tag is automatically generated. Enum and Input Object type definitions are also automatically generated.
./src/types/GetFilms.ts
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.

// ====================================================
// GraphQL query operation: GetFilms
// ====================================================

export interface GetFilms_allFilms_films {
  __typename: "Film";
  /**
   * The episode number of this film.
   */
  episodeID: number | null;
  /**
   * The title of this film.
   */
  title: string | null;
}

export interface GetFilms_allFilms {
  __typename: "FilmsConnection";
  /**
   * A list of all of the objects returned in the connection. This is a convenience
   * field provided for quickly exploring the API; rather than querying for
   * "{ edges { node } }" when no edge data is needed, this field can be be used
   * instead. Note that when clients like Relay need to fetch the "cursor" field on
   * the edge to enable efficient pagination, this shortcut cannot be used, and the
   * full "{ edges { node } }" version should be used instead.
   */
  films: (GetFilms_allFilms_films | null)[] | null;
}

export interface GetFilms {
  allFilms: GetFilms_allFilms | null;
}
It is useQueryOK if you pass this as the type argument of.
const GET_FILMS = gql`
  query GetFilms {
    allFilms {
      films {
        title
        episodeID
      }
    }
  }
`

const Contents: React.FC = () => {
  const { loading, error, data } = useQuery<GetFilms>(GET_FILMS)

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error :</div>
  if (!data) return <div>Error : data is undefined</div>

  return (
    <ul>
      {data.allFilms!.films!.map(film => (
        <li>
          episode {film!.episodeID}: {film!.title}
        </li>
      ))}
    </ul>
  )
}

in conclusion

By using Apollo's ecosystem, you can see that the implementation of the client application that uses GraphQL can be saved. You can see the same contents as the one introduced this time in the session of GraphQL Summit 2019, so please take a look.

0 コメント:

コメントを投稿