グラフプライマー


GraphSQLは、現在、ウェブ開発の最もホットなトピックの一つです.私はMagentoで多くの仕事をします、そして、彼らはフロントエンド開発のために使われる全く新しいgraphql APIを最近発表しました.GraphSQLを学ぶI built an experimental Magento frontend using Next.js . そのプロジェクトの後、私はまだGraphSQLサーバーの構築方法を理解していませんでした.その結果、ノードを使用した例のGraphSQLサーバーを構築することにしました.js元のGraphSQLの仕様は、JavaScriptで書かれたので、それは良い言語を使用するときには、GraphSQLを学習することです.私はgithubリポジトリを作成しましたgraphql-nodejs-example プロジェクト全体を表示する場合.このポストでは、私が本当にそれがどのように働くかについて理解するのを助けたGraphqlについてのいくつかの概念について議論したいです.

GraphSQLサーバーには、1つのエンドポイントしかありません
REST APIでは、単一のリソースに対して複数のURLを持つことが一般的です.データの読み込みや作成にはいくつかのエンドポイントがあるかもしれません/tasks , tasks/1 , /tasks/create . GraphQLでは、サーバーは通常、ルート上で単一のエンドポイントのみを実行します/ または/graphql . クエリをGraphSQLサーバーに送信するときは、要求本文で必要なリソースを明示的に設定するため、サーバーが返す値を決定できます.

GraphSQLは約
GraphSQL APIでは、型言語を使用しているリソースを定義します.グラフィカルサポートfive scalar types より複雑なオブジェクトタイプを構成するために使用できます.つのスカラー型は以下の通りです.Int , Float , String , Boolean and ID . リソースを作成するには、そのオブジェクト型をビルドします.私はフォーラムをエミュレートしたかったので、3つのリソースを作成しました.User , Thread and Comment . GraphSQL型では、これらのリソースは次のようになります.
type User {
  id: Int!
  userName: String!
  firstName: String
  lastName: String
}

type Thread {
  id: Int!
  name: String!
  description: String!
  user: User!
  comments: [Comment]
}

type Comment {
  id: Int!
  description: String!
  user: User!
}
オブジェクトを使用して作成することができますtype キーワード以降の名前.Currentブレースでは、プロパティの名前を入力してコロンと型によってオブジェクトのプロパティを定義します.感嘆符! プロパティの後に値がnullにできないことを示します.
他のカスタムタイプでカスタム型を使用することもできます.The Thread Typeには、ユーザーが作成した他の2つの型を参照するコメントプロパティがあります.タイプ名の周りのブラケット[Comment] プロパティが配列であることを示します.
サーバーを書くとき、どこでこれらのタイプを置きますか?私はそれらをすべてファイルに入れましたschema.graphql とアポロサーバヘルパーを使用gql そのファイルを私のサーバーにインポートします.

リクエストはクエリと突然変異型
Graphqlでは、GraphSQLサーバーに送る2種類のリクエストがあります.query and mutation . エーquery はデータとmutation は、作成または更新のようなデータに対する操作を実行するために使用されます.サーバースキーマでは、次のようにクエリオブジェクト型と突然変異オブジェクト型を定義します.
type Query {
  thread(id: Int!): Thread
  threads: [Thread]
}

type Mutation {
  createThread(name: String!, description: String!, userId: Int!): Thread
  createComment(userId: Int!, threadId: Int!, description: String!): Comment
}
あなたは私の中で見ることができますQuery タイプ、私はスレッドを取得する2つの方法を定義します.The threads propertyすべてのスレッドの配列を返し、thread(id: ID!) 単一のスレッドを返します.括弧は、クエリで渡される引数を示します.以来マークid としてInt , 単一のスレッドを取得するにはid を返します.
Mutation Typeは、スレッドを作成し、コメントを作成するための2つのプロパティがあります.各操作は、リソースを作成するための値のセットを必要とし、それぞれが新しく作成されたリソースを返します.

クエリと突然変異の解決
スキーマを定義した後、データソースからリソースを読み込むロジックを実装するには?あなたはリゾルバを使用してください!リゾルバはREST APIのコントローラに似ています.ごとにQuery and Mutation プロパティを使用すると、引数を受け入れるJavaScript関数を作成し、リソースを操作してデータを読み込むか、またはそれを変更します.
私はApollo Server 私のGraphSQL APIをビルドするライブラリ.ライブラリを使用すると、スキーマを作成し、それをインポートし、すべての要求を処理するリゾルバーオブジェクトを渡すことができます.
私のアポロサーバの設定は次のようになります.
const fs = require('fs');
const { ApolloServer, gql } = require('apollo-server');
const schema = fs.readFileSync(__dirname.concat('/schema.graphql'), 'utf8');
const typeDefs = gql(schema);
const resolvers = require('./resolvers');

const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`)
});
私のアポロサーバインスタンスのために必要なのは、スキーマとリゾルバを渡すことです.
マイリゾルバファイルは、JavaScriptオブジェクトをクエリと突然変異プロパティでエクスポートします.これは、スキーマ内で定義されている各プロパティの関数への参照を保持します.
const threads = require('./threads');
const comments = require('./comments');

module.exports = {
  Query: {
    threads: threads.all,
    thread: threads.findOne,
  },
  Mutation: {
    createThread: threads.create,
    createComment: comments.create,
  }
};
The threads and comments インポートは、それぞれResolverオブジェクトに渡すことができる関数のオブジェクトを返します.
では、リゾルバ関数はどのように見えますか?すべてを返すクエリリゾルバThread データベースからの型
exports.all = async function () {
  const threads = await db.Thread.query().eager('[comments.[user], user]');

  return threads;
};
この関数はデータベースを解決し、Thread そして、アポロサーバはそれが必要とする価値を引き出して、すべての糸を要求したクライアントにそれを返します.
突然変異は非常によく似ています.
exports.create = async function (parent, args) {
  const thread = await db.Thread.query().eager('user').insertAndFetch({
    userId,
    name,
    description,
  } = args);
  thread.comments = [];

  return thread;
};
レゾルバ関数が受け取る2番目のパラメータは、リクエストから渡されたすべての引数です.これらの引数を使用してデータベースに新しいスレッドを作成し、アポロサーバのデータをクライアントに返すようにします.

サーバのクエリ
GraphSQL APIをテストする多くの方法があります.私のようにInsomnia . 開発モードでは、アポロサーバは、不眠症がそれを読むことができるように、あなたのスキーマを返すでしょう、APIのための自動完全な質問をすることができます.
上記のスキーマを使用してサーバーに送信する例を示します.
query getThreads {
  threads {
    id
    name
    description
    user {
      id
      firstName
      lastName
      userName
    }
    comments {
      id
      description
      user {
        id
        userName
      }
    }
  }
}
クエリでは、threads クエリオブジェクトのプロパティと、各スレッドに必要な属性を渡します.動的クエリは、GraphSQLがとても良いものになるものです.なぜなら、APIが提供するのとほとんど同じくらい多くのデータを求めることができるからです.以下のJSONはAPIサーバがクライアントに返すものを表します.
{
  "data": {
    "threads": [
      {
        "id": 1,
        "name": "Thread 1",
        "description": "This is the first thread",
        "user": {
          "id": 1,
          "firstName": "Test",
          "lastName": "User 1",
          "userName": "testuser1"
        },
        "comments": [
          {
            "id": 1,
            "description": "This is a comment on the first thread",
            "user": {
              "id": 2,
              "userName": "testuser2"
            }
          },
          {
            "id": 3,
            "description": "Another comment",
            "user": {
              "id": 1,
              "userName": "testuser1"
            }
          }
        ]
      },
      {
        "id": 2,
        "name": "Thread 2",
        "description": "This is the second thread",
        "user": {
          "id": 2,
          "firstName": "Test",
          "lastName": "User 2",
          "userName": "testuser2"
        },
        "comments": [
          {
            "id": 2,
            "description": "This is a comment on the second thread",
            "user": {
              "id": 1,
              "userName": "testuser1"
            }
          }
        ]
      }
    ]
  }
}
スレッドを作成するための突然変異クエリは次のようになります.
mutation createThread {
  createThread(
    userId: 1,
    name: "A new thread",
    description: "This is a description"
  ) {
    id
    name
    description
    user {
      id
      firstName
      lastName
      userName
    }
  }
}
私は、呼び出していますcreateThread 必要な引数での突然変異型とパスのプロパティ.この関数は、作成したリソースを返します.
{
  "data": {
    "createThread": {
      "id": 7,
      "name": "A new thread",
      "description": "This is a description",
      "user": {
        "id": 1,
        "firstName": "Test",
        "lastName": "User 1",
        "userName": "testuser1"
      }
    }
  }
}

いくつかの一般的なヒントやトリック
ここでは、GraphSQL Serverプロジェクトを起動するためのより一般的なヒントを示します.
  • データベースを使用する場合は、MongoDBやSQLデータベースのようなNoSQLデータベースを使用して、熱心な読み込みをサポートしています.GraphSQLの種類は、ネストされたオブジェクトを使用することが多いので、SQLクエリを作成してデータをマップすることが難しくなります.私はObjection.js SQLiteとORMと私のデータベースのコードをはるかに簡単になりました.
  • GraphSQLは、APIに渡された引数のデータ型を正しく検証しますが、型を検証するだけです.デフォルトでは、文字列型は空または任意の長さにすることができます.私は異議の検証機能を使用しました.突然変異の空のストリングの使用を防ぐJS.
  • The ID スカラー型はid値を文字列に変換します.これはいくつかのデータベースではうまく動作しますが、私の場合はSQLiteを数値キーで使用していましたので、Int .

  • 結論
    私は、特にアポロサーバと異議のようなライブラリの助けを借りて、特にあなたがgraphql APIを構築することができることに驚きました.js私は本当にあなたの利用可能なリソースのための自然なドキュメンテーションになるタイプのまわりでAPIを定義することができます.URLのルーティングまたは型の検証を設定する必要はないだけでなく、多くの時間を節約できます.APIクライアントを構築するためのGraphSQLの利点は広く宣伝されてきましたが、サーバーにはいくつかの本当の利点があると思います.
    私は、この記事があなたがよりよくGraphSQLサーバを理解するのを援助したことを望みます.あなたがこのポストについてのどんな質問または考えもあるならば、コメントを残してください!