エンジニアを目指す日常ブログ

日々勉強したことのメモ。独学ですので間違っていたらコメント等で教えてください。

GraphQLって何?

はじめに

GraphQLとは、Facebookが開発した、REST-APIに変わるAPIの方式

ざっくりと「GraphQLって何?」が一言でわかるように、簡潔にまとめる。

GraphQLの特徴

  • REST-JONと違って、「必要なものを指定して」要求することができる。
  • 応答は普通にJSON形式。

RESTより優れている部分

  • 不要なデータが返却されない分軽くなる。
  • 要求側で必要なものを指定するので、APIのエンドポイント(URL)を分ける必要性が低い。そのため、欲しい情報をすべて取りきるために何度もAPIを投げる必要が無くなる。

要求と応答

GraphQLの要求と応答例

GraphQLではこんな感じで要求する。

query{
  feed{
    id
  }
}

応答は以下のように返却される。

{
  "data": {
    "feed": [
      {
        "id": 1
      },
      {
        "id": 2
      }
    ]
  }
}

他にも情報が欲しい場合は、以下の要求をすると、

query {
  feed {
    id
    url
    description
  }
}

このように返ってくる。

{
  "data": {
    "feed": [
      {
        "id": 1,
        "url": "www.howtographql.com",
        "description": "1つめのリンク。"
      },
      {
        "id": 2,
        "url": "graphql.org",
        "description": "2つめのリンク。"
      }
    ]
  }
}

【比較対象】REST-JSONの要求と応答例

一方で、REST-APIではこんな感じで要求する。

https://jsonplaceholder.typicode.com/posts/1

応答が返ってくる。

// メッセージボディ部
{
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

このように、「idが1である投稿の情報を全部ちょうだい」という場合は問題ないが、画面によっては、「全部の投稿の情報が欲しいけど、ユーザIDとタイトルだけでいい」などの要望が出てくる。

その場合、いちいち要望に応えてエンドポイントを作るのは不可能。 結果受け取る側は、タイトルや本文などの無駄な情報をたくさん受け取ることになってしまう。

Facebookでは、「友達の友達の友達の…」などの情報があるのでREST-JSONでは対応しきれなかったらしい。

要求電文に引数を指定する

以下の記事に記載した。

tomiko0404.hatenablog.com

要求電文の作り方(GraphQL APIの使い方)

APIの内部構造をわかった上で要求電文を書く必要がある。

要求電文には3つの方式(ルートタイプ)がある。

  • Query (照会)
  • Mutation (更新)
  • Subscription (データ検知してプッシュ)

上記の例では、APIサーバ内部で以下のように定義している。

type Link {
  description: String!
  id: Int!
  url: String!
}

type Query {
  feed: [Link!]!
}

既に提供されているGraph QLのAPIには、「GraphiQL」というツールを使って公開されているものがある。これを使うと、データ構造や条件式の必須などが視覚的にわかるので、簡単にリクエストを作成できる。

Facebookが作成している「Space-X」というAPIでもGraphiQLが公開されている。 api.spacex.land

ここで、Queryhistoryフィールドを使ってみる。(これをルートフィールドと言うらしい) ドキュメント(画面右側)を見ると、引数としてID型のidフィールドを設定する必要がある。そして、historyフィールドの型はHistoryであることがわかる。

SpaceX APIの例
SpaceX APIの例

History型のフィールドは以下。

details: String
event_date_unix: Date
event_date_utc: Date
id: ID
links: Link
title: String
flight: Launch

ここから、idを指定して、iddetailsを取得する要求電文を作ると

query Query {
  history(id:2) {
    details
    id
  }
}

となる。

応答は

{
  "data": {
    "history": {
      "details": "NASA awards SpaceX $1.6B Commercial Resupply Services (CRS) contract.",
      "id": "2"
    }
  }
}

となる。

アプリでの使い方例

実際にGatsby.jsでGraphQL APIを使った例が以下。

Gatsby.jsの useStaticQueryを使った場合

const data = useStaticQuery(graphql`
// クエリ
`)

と記載することで、data変数に取得した結果が入る。

about.js

import { useStaticQuery, graphql } from 'gatsby';
import * as React from 'react'
import MyLayout from '../components/myLayout';

const About = () => {

    const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

    return (
        <MyLayout pageTitle="このサイトについて">
            <p>{data.site.siteMetadata.title}</p>
        </MyLayout>
    )

}

export default About;

Gatsby.jsのqueryを使った場合

export const query = graphql`
// クエリ
`;

と記載すると、同じコンポーネントpropsオブジェクトに自動的に値が入ってくる。

そのためこの記載は1コンポーネントにつき1個しか使えない。

blog-tutorial.js

import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import * as React from "react";
import MyLayout from "../components/myLayout";

const BlogTutorial = (props) => {
    const { data } = props;
    console.log(data);
    return (
        <MyLayout pageTitle="ブログ一覧">
            {data.allMdx.nodes.map((node) => (
                <article>
                    <h2>{node.frontmatter.title}</h2>
                    <p>Posted: {node.frontmatter.date}</p>
                    <MDXRenderer>
                        {node.body}
                    </MDXRenderer>
                </article>
            ))}
        </MyLayout>
    );
};

export const query = graphql`
    query {
        allMdx(sort: { fields: frontmatter___date }) {
            nodes {
                frontmatter {
                    title
                    date(formatString: "YYYY年MM月DD日")
                }
                id
                body
                parent {
                    ... on File {
                        modifiedTime
                    }
                }
            }
        }
    }
`;

export default BlogTutorial;

参考: tomiko0404.hatenablog.com

おわりに

GraphQLはGatsbyにも利用されているので、理解したいが、かなり奥が深そう。 Apollo-serverNexusを利用して、GraphQLのAPIを提供するWebサーバも作ってみようと思っているので、理解出来たら記事にしたい。

参考
Building a GraphQL Server with TypeScript & Apollo

REST-JSONはExpressを使えば

app.get("/", (req, res) => {
    res.status(200).send({ id: 1, message: "メッセージ" });
});

これだけでAPIを作れたのだが、GraphQLは難しい。