この記事では、GraphQLAPIで独自のデータ構造を作成する方法について説明します。 これから説明することはすべて、公式のGraphQLドキュメントで詳しく調べることができます。

前提条件

基本的なAPIを設定しているので、最初にクエリを作成してデータをテストする方法を知っておくのが最善です。

インストール

簡単にするために、Preprosまたはbabelを使用することをお勧めします。これにより、importなどの最新のJavaScript機能を使用できます。

Preprosを使用する場合は、server.jsファイルでBabelおよびAutoCompileオプションがオンになっていることを確認してください。

サーバーのセットアップには、graphql-yoganodemonを使用します。 nodemon ファイルの変更時に自動サーブをリロードできるようになり、 graphql-yoga ExpressベースのGraphQLサーバーをセットアップするための簡単なツールを提供します。

$ npm i graphql-yoga nodemon

nodemonセットアップ

取得から始めましょう nodemon 働く。 下 scripts プロジェクトの package.json ファイル、追加するだけです "start": "nodemon server.js"、実行する代わりに node server 実行します npm run start.

package.json
{
  "name": "graphql-api",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "dependencies": {
    "graphql-yoga": "^1.16.7"
  },
  "devDependencies": {
    "nodemon": "^1.19.1"
  },
  "scripts": {
    "start": "nodemon server.js"
  },
  "author": "",
  "license": "ISC"
}

サーバーのセットアップ

メインファイルにインポートします GraphQLServer から graphql-yoga APIを動作させるため localhost:4000. サーバーには、データを初期化して構造化する型定義と、サーバーにデータの取得とフィルタリングの方法を指示するリゾルバーの2つが必要になります。 物事を整理するために、定義とリゾルバーの変数を作成し、それらをサーバー関数に渡します。

server.js
import { GraphQLServer } from 'graphql-yoga'

const typeDefs = ``; // Our definitions need to be in template literals

const resolvers = {};

const server = new GraphQLServer({ typeDefs, resolvers }) ;

server.start(() => console.log('server running'));

でスキーマを設定していないため、今のところエラーが返されます typeDefs まだ。

タイプ定義

追加するだけで基本的なクエリを設定できます type Query {} 返されるデータのタイプで利用できるようにしたいデータのリストを使用します。 独自に作成することもできますが、GraphQLにはいくつかのベイクインタイプが付属しています。 String, Int (すべて実数)、 Float (10進数)、 Boolean, ID、および角かっこで囲まれたこれらのいずれも、そのタイプの配列です。 で終わるすべてのタイプ ! null許容型として設定されているため、何も返されない場合はエラーが返されます。

考えられるさまざまなバリエーションの例を次に示します。 これは説明のみを目的としているため、同じフィールドに複数のバリエーションがないことに注意してください。

server.js
const typeDefs = `
  type Query {
    user: String      # null
    user: String!     # error 'Cannot return null for non-nullable field Query.user'
    users: [String!]! # must return an array of non-null strings
  }
`;

以上で localhost:4000 これらのいずれかを入力してもエラーは発生しなくなりますが、データの取得方法についてはまだ説明していないため、クエリを実行するとエラーまたはnullが発生します。

レゾルバ

データを取得できるようにするには、クエリ対象と同じ名前の関数を作成し、必要なものを返すようにする必要があります。

server.js
const resolvers = {
  Query: {
    user(){
      return 'I am a string';
    },
    users(){
      const names = ['Chomp', 'Jaws', 'Alli'];
      return names;
    }
  }
};

カスタムタイプ

のようなタイプ String, Int、 と Boolean すぐに使用できるGraphQLに付属しているものですが、より複雑なデータを返す独自の型を作成し、その型をクエリに渡して要求可能にすることもできます。

の外 Query しかし、まだ typeDefs 私たちは自分のタイプを次のように作ることができます User (カスタムタイプを大文字にするのがベストプラクティスです)そして、必要なデータを設定します。 のように Query、私たちのタイプの各アイテムも、次のように入力する必要があります age: Int!、および対応するリゾルバによって返されるデータがあります。

server.js
const typeDefs = `
  type Query {
    user: User! # Must be of our custom type, User
  }

  type User {
    name: String!
    age: Int!
    friends: [String]!
  }
`;

そして、リゾルバーで、データを含むオブジェクトを返すことができます。

const resolvers = {
  Query: {
    user(){
      return {
        name: 'Chomp',
        age: 47,
        friends: ['Alli', 'Jaws']
      };
    }
  }
};

データ設定

実際のシナリオでは、このようなリゾルバーですべてのデータを返すのではなく、実際にはデータベースなどの他のソースからデータを取得します。 バックエンドについて心配する代わりに、使用するダミーデータを設定するだけです。

const users = [
  {
    name: 'Chomp',
    age: 47,
    friends: ['Alli', 'Jaws']
  },{
    name: 'Alli',
    age: 16,
    friends: ['Chomp', 'Jaws']
  },{
    name: 'Jaws',
    age: 35,
    friends: ['Alli', 'Chomp']
  },
];

一連のユーザー全体を取得するだけなので、次のように伝えます。 typeDefs タイプの配列が必要です User リゾルバーはユーザー配列全体を返すだけです。

const typeDefs = `
 type Query {
    user: [User!]! 
  }

  type User {
    name: String!
    age: Int!
    friends: [String]!
  }
`;

const resolvers = {
  Query: {
    user(){
      return users;
    }
  }
};

引数

現在、私たちの唯一のオプションは、1つの特定のデータ、すべてを返すことです。 応答を必要なものに絞り込みたい場合はどうなりますか?

これを行うには、クエリで引数を設定し、それらをリゾルバーの条件ステートメントで使用します。 引数も入力する必要があります。

const typeDefs = `
 type Query {
    user(name: String!): [User!]! 
  }
`;

リゾルバーでは、GraphQlが提供するいくつかの非常に便利なオブジェクトにアクセスできます。 今のところ必要なのは parentargs.

  • parent:カスタムタイプをネストした場合の親要素に関する情報。 だから私たちが持っていた場合 User アクセスしたタイプ Post タイプすると、各投稿はユーザーのデータにアクセスできます。
  • args:すべての引数がクエリに渡されました。
  • ctx context の略で、認証データのようにクエリが行うものはすべて必要になる場合があります。
  • info:クエリの状態に関する情報。

ここでは、議論があるかどうかを確認します args ある場合は、JavaScriptを使用します filter() 名前が引数と一致するユーザーのみを返します。

server.js
const resolvers = {
  Query: {
    user(parent, args, ctx, info){
      if (!args.name) return users;

      else return users.filter(user => {
        return user.name.toLowerCase().includes(args.name.toLowerCase());
      });
    }
  }
};

以上で localhost:4000 私たちはこのように私たちの議論を利用することができます:

{
  user(name: "alli") {
    name
    friends
  }
}

リレーショナルデータ

より多くのネストされたデータを許可するために、カスタムタイプを他のカスタムタイプに追加できます。 その場合、外部に別のリゾルバーを追加する必要があります Query そのタイプのデータの場合。

ユーザーを取得するたびに、各友達のユーザーデータを取得できるようにします。

server.js

私たちのリゾルバーは空のオブジェクトを作成し、各友達をループします parent 一致するすべてのユーザーを friends 配列、および戻り値 friends 私たちのクエリに。

const typeDefs = `
 type Query {
    user(name: String!): [User!]!
  }

  type User {
    name: String!
    age: Int!
    friends: [User!]!
  }
`;

const resolvers {
  Query: { ... },
  User: { // The nested type that we're querying for
    friends(parent, args, ctx, info) { 
        const friends = [];

        parent.friends.forEach(friend => users.filter(user => {
          if (user.name.toLowerCase().includes(friend.toLowerCase())) friends.push(user);
        }));

        return friends;
    }
  }
};

結論

GraphQLで探求することはまだまだたくさんありますが、これが独自のAPIをセットアップするための十分な入門書として機能したことを願っています。

この例を正しく機能させるのに問題がある場合は、いつでもこのリポジトリを参照できます。