パソナについて
記事検索

Apollo Server を使って 全件取得とid指定取得のできる 単純なGraphQLサーバを作る

株式会社パソナ クラウドソリューション第1チームの米永です。
この記事では、Apollo Serverを使ってGraphQLサーバを作る方法を紹介します。

Apollo Server を使って 全件取得とid指定取得のできる 単純なGraphQLサーバを作る

株式会社パソナ クラウドソリューション第1チームの米永です。
この記事では、Apollo Serverを使ってGraphQLサーバを作る方法を紹介します。

知識・情報

2025/09/26 UP

前提知識

・クライアント/サーバモデルの概念を知っているか、なんとなく理解している
・端末エミュレータ、CLIを使用できる
・Web開発におけるAPIやREST等の概念を知っている
・node.jsやnpmを知っている
・JavaScriptの基本的な文法を知っている

GraphQLとは

GraphQLは、Facebookによって作られオープンソースで公開されている、APIのためのクエリ言語です。GraphQLはRESTで発生しがちなオーバーフェッチ問題・アンダーフェッチ問題を解決するために作られました。グラフ状のデータを取得するためのクエリ言語であるためGraphQLと名付けられています。

REST APIではパフォーマンスに不満がある場合にGraphQLを導入することで改善が期待できます。

Apollo Serverとは

Apollo ServerはNode.js上で動作するオープンソースのGraphQLサーバです。
npmからApollo Serverパッケージをインストールし、Node.js上で実行するコード内でApollo Serverのインスタンスを作成・起動することでサーバとして動作します。

この記事でやること

この記事では、Apollo Serverを使用して単純なGraphQLサーバを作成する例を紹介します。このGraphQLサーバはデータの全件取得とid指定での取得ができるものとします。
サーバの全件取得を行うまでの流れは、Apollo Serverのチュートリアルとほぼ同じ内容になります。
apollo server 公式チュートリアル

この記事では、それに加えてid指定での取得機能の追加を行ない、curlでHTTPリクエストを投げて動作確認します。

この記事でやらないこと

通常GraphQLサーバはDBからのデータ取得などを行いますが、この記事では扱う範囲外とします。
また、GraphQLサーバのようなAPIサーバを立てたのであれば通常はそれを利用するクライアントやWebフロントエンドが存在するか開発することになるかと思いますが、そちらもこの記事では扱いません。

作業前提

・npm がインストールされていること(本記事の例では11.4.2を使用します)
・適当なテキストエディタ・端末エミュレータがインストールされていること
・本記事ではMacOS X / zshを使用します

手順

プロジェクトの作成と必要なパッケージのインストール

npmプロジェクトの作成

お好みのターミナルエミュレータを開き、任意の場所にプロジェクト用のディレクトリを作成し、その中に移動します。今回はgql-server-sampleとします

mkdir gql-server-sample
cd gql-server-sample
ES Moduleが使えるように設定します

npm pkg set type="module"

今回のサンプルではプロジェクトルートのindex.jsに処理を記述します。 npm run startindex.jsを起動できるよう、package.jsonのscripts内に設定を行います。scriptsの部分は以下のようになります。

"scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },


必要なパッケージのインストール

GraphQLとApollo Serverをインストールします

npm install @apollo/server graphql

image


インストールが完了し、このような表示になるはずです。
筆者の環境ではApollo Server 4.12.2とGraphQL 16.11.0がインストールされました。

最初の実装とサンプルデータの用意

プロジェクトの用意ができたので、早速GraphQLサーバの動作を記述していきます。
プロジェクトルートにindex.jsファイルを作成し、お好みのエディタで編集してください。package.jsonに記述した通り、後ほどnpm startを実行することでこのファイルが実行されます。

スキーマの定義

GraphQLサーバでは、ユーザが何をクエリできるかを決める「スキーマ」を定義します。
今回は例として不動産物件を取り扱うサイトを想定してみます。価格・場所・間取りを持つ物件を表す型を、price, place, layoutを持つProperty型として定義します(ここではPropertyはコンピュータ用語ではなく「物件」の訳語として使用しています)。物件を一意に区別するためのIDも必要でしょう。

index.jsに以下のようにコーディングします。

const typeDefs =`#graphql
    type Property {
        id: Int
        price: Int
        place: String
        layout: String
    }
`


GraphQLではこのようにオブジェクトの型を定義しただけではまだユーザーがそれをクエリできるようになるわけではありません。クライアントが発行できるクエリと、返却されるデータの型をquery型として定義する必要があります。
上記のtypeDefs内、Property型の後に以下のようにQuery型を定義します。
type Query {
        allProperties: [Property],
    }

allPropertiesというクエリの結果としてProperty型のオブジェクトを配列で返すことが表現されています。
これでデータの構造を定義することができました。

サンプルデータの用意

通常の不動産サイトであればDBにデータを格納しているでしょうが、今回はGraphQLサーバそのものの例なのでDBは用意せずハードコーディングしてしまいます。以下のようにデータを用意しました。

const properties = [
    {
        id: 0,
        price: 3990,
        place:  "名古屋市守山区",
        layout: "3LDK"
    },
    {
        id: 1,
        price: 3750,
        place:  "名古屋市港区",
        layout: "4LDK"
    },
        {
        id: 3,
        price: 1850,
        place:  "名古屋市中区",
        layout: "4K"
    },
]


リゾルバの定義

これでデータが定義できました。GraphQLサーバは「ユーザから受け取ったクエリに対してどのような型のデータを返すか」を知っていますが、返すデータをどのように取得するかを知りません。GraphQLでは、リゾルバを定義することによってこれを定義します。わざわざリゾルバを書かなければいけないのは少し冗長に感じるかもしれませんが、逆に言えば開発者がリゾルバを好きに書けることでデータの取得や加工の自由度の高さが確保されているとも言えます。

すでにtypeDefs中のQuery型で定義したallPropertiesについて、properties全体を返すという動作を定義するリゾルバを書きます。リゾルバの実装は以下の通りになります

const resolvers = {
    Query: {
        allProperties: () => {
            return properties
        },
    }
}

サーバインスタンスの作成と起動

記事冒頭で説明した通り、ApolloServerを動作させるためにはコード内でインスタンス化と起動を行います。コードは以下のようになります。

const server = new ApolloServer({
    typeDefs,
    resolvers,
})

const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
})

console.log(`Server ready at: ${url}`)

先に定義したtypeDefsとresolversを渡してサーバのインスタンス化を行い、ポート番号を指定してstartStandaloneServer関数を呼び出します。
この際、インストールしたパッケージからApolloServerとstartStandaloneServerをインポートする必要があります。ファイルの冒頭に以下を書き加えます。
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
また、最後の行ではサーバのURLをコンソールに出力しています。
以上で、動作する最初のGraphQLサーバを記述することができました。ここまでindex.jsに全てのコードを書いてきましたが、最終的なコードは以下のようになります。
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

const typeDefs =`#graphql
    type Property {
        id: Int
        price: Int
        place: String
        layout: String
    }

    type Query {
        allProperties: [Property],
    }
`

const properties = [
    {
        id: 0,
        price: 3990,
        place:  "名古屋市守山区",
        layout: "3LDK"
    },
    {
        id: 1,
        price: 3750,
        place:  "名古屋市港区",
        layout: "4LDK"
    },
        {
        id: 3,
        price: 1850,
        place:  "名古屋市中区",
        layout: "4K"
    },
]

const resolvers = {
    Query: {
        allProperties: () => {
            return properties
        },
    }
}

const server = new ApolloServer({
    typeDefs,
    resolvers,
})

const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
})

console.log(`Server ready at: ${url}`)

全件取得の動作確認

記述したGraphQLサーバを実際に起動してみましょう。プロジェクトルートでコマンド npm startを実行します

image

コンソールに表示されている通り、localhost:4000にブラウザでアクセスすると以下のような表示になりました。

image

この画面では、実際にクエリを書いて発行し、そのレスポンスを確認することができます。実際にクエリを書いてみましょう。
allPropertiesクエリを使用し、id、price、placeの全てを取得するクエリは以下の通りです。

query {
  allProperties {
    id
    price
    place
  } 
}
クエリを書いたら「Run」ボタンをクリックします。
成功すれば、このように全件のデータを含むレスポンスが右側に表示されます。

image

ID指定取得の実装

今回の記事では、このサンプルにもう一つ機能を追加したいと思います。タイトルにある通り、id指定でのデータ取得です。現在データには3つの物件がありますが、そのうちの一つだけを取得できるようにします。

typeDefsでのクエリの追加

現在、typeDefsではユーザの実行できるクエリとして allProperties のみが定義されています。今回は一件だけ取得できるようにしたいので、そのようなクエリを追加します。

    type Query {
        allProperties: [Property],
        property(id:Int!): Property,
    }
「nullでない整数型のidを渡してpropertyクエリを実行するとProperty型のオブジェクト単体を返す」と定義されていることがわかるかと思います。
なお、GraphQLではIDを表現するのに使用する型としてID型というものがあります。実際のプロダクトでIDを扱う場合はそちらの使用も検討してください。

リゾルバの追加

全件取得と同じく、typeDefsでのクエリ定義の他にリゾルバの実装も必要です。以下のように、propertiesの中からidが一致するものを返すリゾルバを実装します。
resolversにid指定で取得するpropertyリゾルバを追加したものが以下のコードになります。
クエリで渡された引数は、リゾルバが受け取る引数のうち、argsの中にあります。 args.idがpropertiesの要素のフィールド idと一致するものを返すようにします。

const resolvers = {
    Query: {
        allProperties: () => {
            return properties
        },
        property: (parent, args, contextValue, info) => {
            return properties.find((property) => property.id === args.id)
        }
    }
}
以上を踏まえたindex.js全体は以下のようになります。
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

const typeDefs =`#graphql
    type Property {
        id: Int
        price: Int
        place: String
        layout: String
    }

    type Query {
        allProperties: [Property],
        property(id:Int!): Property,
    }
`

const properties = [
    {
        id: 0,
        price: 3990,
        place:  "名古屋市守山区",
        layout: "3LDK"
    },
    {
        id: 1,
        price: 3750,
        place:  "名古屋市港区",
        layout: "4LDK"
    },
        {
        id: 3,
        price: 1850,
        place:  "名古屋市中区",
        layout: "4K"
    },
]

const resolvers = {
    Query: {
        allProperties: () => {
            return properties
        },
        property: (parent, args, contextValue, info) => {
            return properties.find((property) => property.id === args.id)
       }
    }
}

const server = new ApolloServer({
    typeDefs,
    resolvers,
})

const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
})

console.log(`Server ready at: ${url}`)

ID指定取得の動作確認

実行中のサーバを端末エミュレータからctrl+Cで停止し、再度npm startで起動します。
再度localhost:4000にアクセスし、以下の通りidが1の物件の金額・場所・間取りを取得するクエリを書き、Runボタンをクリックします。

query {
  property(id:1) {
    price
    place
    layout
  } 
}

image

無事、idが1の物件のデータを取得することができました。

Curlを使った動作確認

GraphQLはWeb APIを念頭に置いた仕組みですので、任意のクライアントからのHTTPリクエストに対応できるはずです。 curlでのクエリ実行を試してみます。
curlのインストールされた環境で、端末エミュレータから以下を実行します。

curl 'http://localhost:4000/' \
 -H 'Content-Type: application/json' \
 --data '{"query": "{ property(id: 1) { price place layout}}"}'

うまくいけば、以下のようにレスポンスが返ってくるはずです。

image

まとめ

以上で、Apollo Serverを使って全件取得とid指定取得が行えるGraphQLサーバを構築することができました。
記事中でも書いた通り、実際のアプリ開発ではGraphQLが取得するデータのソースとなるDBを用意したり、フロントエンドやクライアントの開発も行うことになるかと思います。また、GraphQLやApollo Serverの機能についてもほんの初歩を紹介したにすぎませんので、深掘りの余地があります。

今回の記事ではシンプルなGraphQLサーバを実装しましたが、「これだけのことを行うのにスキーマを定義したりリゾルバを実装したり、やらなければいけないことが多くて煩雑」と感じたかもしれません。GraphQLのメリットはパフォーマンス面やドキュメント作成の容易さにあり、今回のようなシンプルな例ではメリットを感じにくいです。実際にGraphQLを採用するに当たっては自身のプロダクトの規模や特性・課題に合わせて導入の是非を検討してください。

今後の記事として、今回作成したサーバを利用したフロントエンドの実装などを予定していますのでご期待いただければ幸いです。

この記事を書いた人
image
クラウドソリューション第1チーム 米永

Webアプリの開発を行う傍ら、社内の有志チームでUnityを使ったゲームコンテンツの制作を行っています。Unityを使ったゲーム・VRコンテンツ開発に関する記事もありますので、ご興味があればそちらもご覧ください。
Amplify Shader Editorを使ってスクリーンスペースでテクスチャを描画するマテリアルを作る