GraphQLのスキーマとリゾルバー
この記事では、GraphQLAPIで独自のデータ構造を作成する方法について説明します。 これから説明することはすべて、公式のGraphQLドキュメントで詳しく調べることができます。
前提条件
基本的なAPIを設定しているので、最初にクエリを作成してデータをテストする方法を知っておくのが最善です。
インストール
簡単にするために、Preprosまたはbabelを使用することをお勧めします。これにより、importなどの最新のJavaScript機能を使用できます。
Preprosを使用する場合は、server.jsファイルでBabelおよびAutoCompileオプションがオンになっていることを確認してください。
サーバーのセットアップには、graphql-yogaとnodemonを使用します。 nodemon
を使用すると、ファイルの変更時にサーブを自動的にリロードできます。graphql-yoga
を使用すると、ExpressベースのGraphQLサーバーをセットアップするための簡単なツールが提供されます。
$ npm i graphql-yoga nodemon
nodemonセットアップ
nodemon
を機能させることから始めましょう。 プロジェクトのpackage.json
ファイルのscripts
の下に、"start": "nodemon server.js"
を追加するだけなので、node server
を実行する代わりに、npm run start
を実行します。 。
{
"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つが必要になります。 物事を整理するために、定義とリゾルバーの変数を作成し、それらをサーバー関数に渡します。
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許容型として設定されます。つまり、何も返されない場合はエラーが返されます。
考えられるさまざまなバリエーションの例を次に示します。 これは説明のみを目的としているため、同じフィールドに複数のバリエーションがないことに注意してください。
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が発生します。 。
レゾルバ
データを取得できるようにするには、クエリ対象と同じ名前の関数を作成し、必要なものを返すようにする必要があります。
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!
のように入力する必要があり、対応するリゾルバーからデータが返されます。
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が提供するいくつかの非常に便利なオブジェクトにアクセスできます。 今のところ必要なのはparent
とargs
だけです。
parent
:カスタムタイプをネストした場合の親要素に関する情報。 したがって、Post
タイプにアクセスするUser
タイプがある場合、各投稿はユーザーのデータにアクセスできます。args
:すべての引数がクエリに渡されました。ctx
: context の略で、認証データのようにクエリが行うものはすべて必要になる場合があります。info
:クエリの状態に関する情報。
ここでは、args
に引数があるかどうかを確認し、ある場合は、JavaScriptのfilter()
を使用して、名前が引数と一致するユーザーのみを返します。
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をセットアップするための十分な入門書として機能したことを願っています。
この例を正しく機能させるのに問題がある場合は、いつでもこのリポジトリを参照できます。