この記事では、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を機能させることから始めましょう。 プロジェクトのpackage.jsonファイルのscriptsの下に、"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"
}

サーバーのセットアップ

メインファイルで、graphql-yogaからGraphQLServerをインポートして、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にはいくつかのベイクインタイプが付属しています。 StringInt(すべて実数)、Float(10進数)、BooleanID、およびこれらのいずれか角かっこはそのタイプの配列です。 !で終わる型はすべて、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;
    }
  }
};

カスタムタイプ

StringIntBooleanなどのタイプは、すぐに使用できる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:カスタムタイプをネストした場合の親要素に関する情報。 したがって、PostタイプにアクセスするUserタイプがある場合、各投稿はユーザーのデータにアクセスできます。
  • 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をセットアップするための十分な入門書として機能したことを願っています。

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