CRUDをノード、GraphSQL、および


執筆Avanthika Meenakshi ✏️
Graphqlは、単一の終点にすべての要求を抽象化することによってAPIを構築する複雑さを減らします.伝統的な休息APIとは異なり、それは宣言的です;必要なものは何でも返します.
もちろん、すべてのプロジェクトがGraphqlを必要とするわけではありません.それはよく定義されたスキーマを持っているので、確かに我々はオーバーフェッチしないことを知っている.しかし、安定したRESTful APIシステムを持っていれば、単一のデータソースからデータに依存しています.
例えば、私たちが自分自身のためにブログを作成していると仮定し、単一のMongoDBデータベース内のデータを格納、取得、および通信することを決定します.この場合、我々はアーキテクチャ的に複雑なことをしていないし、Graphqlを必要としない.
一方、複数のソース(例えばMongoDB、MySQL、Postgres、および他のAPI)からデータに依存する本格的な製品があると想像しましょう.この場合、私たちはGraphqlに行きます.
たとえば、我々は自分自身のためのポートフォリオサイトを設計しているし、我々はソーシャルメディアやGithubから(データを投稿してください)のデータを必要とし、我々はまた、ブログを維持するために独自のデータベースを持って、我々はビジネスロジックとスキーマを記述するためにGraphSQLを使用することができます.それは真実の単一の源としてデータを統合します.
一旦我々がフロントエンドに正しいデータを送るためにResolver機能を持っているならば、我々は簡単に一つの源の範囲内でデータを管理することができます.本稿では、Graphqlを使った簡単なエンドツーエンドCRUD操作を実装します.

GrapqlサーバでのCRUD


サーバの設定


我々は、使用して簡単なGraphSQLサーバーをオフにする予定ですexpress-graphql そして、それをMySQLデータベースに接続してください.ソースコードとMySQLファイルはrepository .
GraphSQLサーバーは、スキーマとリゾルバの上に構築されます.最初のステップとして、スキーマを定義します.このスキーマは、全体のアプリケーション構造を説明します.
第二に、スキーマで定義されているものについては、それぞれのレゾルバを構築してデータを計算し、発信する.リゾルバの機能を持つアクションtypedefで宣言されたクエリごとに、データを返すレゾルバを作成します.
最後に、エンドポイントを定義し、設定を渡すことによってサーバー設定を完了します.初期化/graphql 我々のアプリのためのエンドポイントとして.にgraphqlHTTP ミドルウェアは、構築されたスキーマとルートリゾルバを渡します.
スキーマとルートリゾルバと共に、我々はGraphiQL 遊び場GraphSQLは、我々が構築するGraphSQLクエリで遊ぶのを助けるブラウザGraphSQL IDEでインタラクティブです.
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

var root = {
  hello: () => "World"
};

var app = express();

app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));

app.listen(4000);

console.log('Running a GraphQL API server at localhost:4000/graphql');
一度サーバーが行くには、アプリを実行して良いですnode index.js サーバを起動します.http://localhost:4000/graphql . 私たちはこんにちはのために質問することができて、ストリングとして「世界」を得ることができます.

データベースの接続


次のようにMySQLデータベースとの接続を確立します.
var mysql = require('mysql');

app.use((req, res, next) => {
  req.mysqlDb = mysql.createConnection({
    host     : 'localhost',
    user     : 'root',
    password : '',
    database : 'userapp'
  });
  req.mysqlDb.connect();
  next();
});
私たちは複数のデータベース/ソースを接続し、それらをリゾルバで固守させます.私はここで単一のMySQLデータベースに接続しています.私がこの記事のために使用したデータベースダンプはgithubリポジトリにあります.

GraphSQLによるデータの読み書き


データソースのデータを読み書きするためにクエリと突然変異を使用します.この例では、データベースのクエリに役立つ汎用QueryDB関数を定義しました.

クエリ


データを一覧表示して表示するすべてのSELECT文がtype Query typedef.ここで定義されているクエリは二つあります.データベース内のすべてのユーザを一覧表示し、別のユーザをIDSで表示するためのものです.

  • リストデータ:ユーザーをリストするために、我々はGraphSQLスキーマオブジェクトタイプを定義していますUser , これは、我々が得ることができるか、期待するものを表しますgetUsers クエリ.そして、getUsers ユーザーの配列を返すクエリ.

  • 単一の記録を見ること:一つの記録を見るためにid との引数としてgetUserInfo クエリを定義します.データベース内の特定のIDをクエリし、フロントエンドにデータを返します.

  • 今、我々はすべてのレコードを取得し、IDによってレコードを表示するクエリをまとめて、我々はGraphiqlからユーザーのクエリをしようとすると、画面上のユーザーの配列を一覧表示されます!🙂
    var schema = buildSchema(`
      type User {
        id: String
        name: String
        job_title: String
        email: String
      }
      type Query {
        getUsers: [User],
        getUserInfo(id: Int) : User
      }
    `);
    
    const queryDB = (req, sql, args) => new Promise((resolve, reject) => {
        req.mysqlDb.query(sql, args, (err, rows) => {
            if (err)
                return reject(err);
            rows.changedRows || rows.affectedRows || rows.insertId ? resolve(true) : resolve(rows);
        });
    });
    
    var root = {
      getUsers: (args, req) => queryDB(req, "select * from users").then(data => data),
      getUserInfo: (args, req) => queryDB(req, "select * from users where id = ?", [args.id]).then(data => data[0])
    };
    

    突然変異


    データベースの書き込み操作、更新、削除-一般的に突然変異の下で定義されます.突然変異は、graphqlエンジンによって逐次的に実行される.クエリは並列実行されます.
  • データの作成:突然変異を定義しました.createUser , 指定した引数を指定してMySQLデータベースにデータを作成します.
  • データの更新または削除:レコードを表示する場合と同様です.updateUserInfo ) と削除deleteUser ) IDをparamとしてデータベースを変更します.
  • 関数は、変更が起こったかどうかを示すためにbooleanで解決します.
    var schema = buildSchema(`
      type Mutation {
        updateUserInfo(id: Int, name: String, email: String, job_title: String): Boolean
        createUser(name: String, email: String, job_title: String): Boolean
        deleteUser(id: Int): Boolean
      }
    `);
    
    var root = {
      updateUserInfo: (args, req) => queryDB(req, "update users SET ? where id = ?", [args, args.id]).then(data => data),
      createUser: (args, req) => queryDB(req, "insert into users SET ?", args).then(data => data),
      deleteUser: (args, req) => queryDB(req, "delete from users where id = ?", [args.id]).then(data => data)
    };
    
    今、我々は設定して、物事のサーバー側をソートし、試してみて、バックエンドを我々の反応アプリに接続してみましょう.

    GrapqlクライアントによるCRUD


    一度サーバーを配置すると、クライアントロジックを作成し、データを突然変異を作成する簡単です.アポロクライアントの状態管理とキャッシュに役立ちます.また、非常に抽象化され、迅速です:あなたのデータを取得するためのロジックのすべての、ロードとエラー状態を追跡し、UIを更新するにはuseQuery フック.

    Grapqlサーバへの接続


    私はCRAのボイラープレートを作成してインストールしているGraphQL , apollo-boost , and @apollo/react-hooks . 我々はアポロクライアントを初期化し、それが反応するフック.
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import ApolloClient from 'apollo-boost';
    import { ApolloProvider } from '@apollo/react-hooks';
    
    const client = new ApolloClient({
      uri: 'http://localhost:4000/graphql'
    });
    
    ReactDOM.render(
      <ApolloProvider client={client}>
        <App />
      </ApolloProvider>,
      document.getElementById('root')
    );
    

    データの読み取りと変異


    私はすべてのGraphSQLクエリを管理していますQueries 私のソースコードのフォルダ.サーバーからデータをリクエストしますuseQuery フックは、反応フックAPIの上に構築されています.これは、UIにデータをもたらすのに役立ちます.
    GraphSQLクエリは一般にgql 関数.gql クエリ文字列をクエリドキュメントに変換するのに役立ちます.ここでどのように我々のアプリのクエリを定義します.
    import { gql } from 'apollo-boost';
    
    export const GET_USERS = gql`
      {
        getUsers {
          id,
          name,
          job_title,
          email
        }
      }
    `;
    
    export const VIEW_USERS = gql`
      query ($id: Int){
        getUserInfo(id: $id) {
          id,
          name,
          job_title,
          email
        }
      }
    `;
    
    export const ADD_USER = gql`
      mutation($name: String, $email: String, $job_title: String) {
        createUser (name: $name, email: $email, job_title: $job_title)
      }
    `;
    
    export const EDIT_USER = gql`
      mutation($id: Int, $name: String, $email: String, $job_title: String) {
        updateUserInfo (id: $id, name: $name, email: $email, job_title: $job_title)
      }
    `;
    
    export const DELETE_USER = gql`
      mutation($id: Int) {
        deleteUser(id: $id)
      }
    `
    
    一度ApolloProvider が設定されているので、GraphSQLサーバーからデータを要求できます.我々は、我々がしようとしているクエリを渡しますuseQuery フック、それは私たちのための結果を提供します.
    引数の有無について二つの問い合わせを行い、フロントエンドのクエリや突然変異の扱い方を示しました.useQuery トラックerror and loading 私たちのために州と関連するオブジェクトに反映されます.サーバーが結果を送信すると、データプロパティに反映されます.
    import React from 'react';
    import { useQuery } from '@apollo/react-hooks';
    import { GET_USERS, VIEW_USERS } from "./Queries";
    import { Card, CardBody, CardHeader, CardSubtitle, Spinner } from 'reactstrap';
    
    function App() {
      const getAllUsers = useQuery(GET_USERS);
      const userInfo = useQuery(VIEW_USERS, { variables: { id: 1 }});
      if (getAllUsers.loading || userInfo.loading) return <Spinner color="dark" />;
      if (getAllUsers.error || userInfo.error) return <React.Fragment>Error :(</React.Fragment>;
    
      return (
        <div className="container">
          <Card>
            <CardHeader>Query - Displaying all data</CardHeader>
            <CardBody>
              <pre>
                {JSON.stringify(getAllUsers.data, null, 2)}
              </pre>
            </CardBody>
          </Card>
          <Card>
            <CardHeader>Query - Displaying data with args</CardHeader>
            <CardBody>
              <CardSubtitle>Viewing a user by id</CardSubtitle>
              <pre>
                {JSON.stringify(userInfo.data, null, 2)}
              </pre>
            </CardBody>
          </Card>
        </div>
      )
    }
    
    export default App;
    
    問合せに類似して、突然変異は同じものを使用するでしょうuseQuery フックとデータをクエリに変数として渡します.
    const deleteMutation = useQuery(DELETE_USER, { variables: { id: 8 }});
    const editMutation = useQuery(EDIT_USER, { variables: { id: 9, name: "Username", email: "email", job_title: "job" }});
    const createMutation = useQuery(ADD_USER, { variables: { name: "Username", email: "email", job_title: "job" }});
    

    結論


    田大!私たちはGraphSQLでエンドツーエンドのCRUD操作をしました.クライアント側では、データを読み書きすることは、反応フックの導入後非常に単純になった.アポロクライアントも認証、より良いエラー処理、キャッシュ、および楽観的なUIの規定を提供します.
    サブスクリプションは、グラフィカルな別の興味深い概念です.BoilerPlateとして、このアプリケーションでは、我々はこれらのような他の概念で実験を続けることができます!
    ハッピーコーディング!
    このポストで何か悪いことを見ている?あなたは正しいバージョンを見つけることができますhere .

    プラグイン:ログオン、WebアプリのDVR





    LogRocket あなたが自分のブラウザで起こったかのように問題を再生することができるフロントエンドのログツールです.代わりに、エラーが発生したり、スクリーンショットやログのダンプのユーザーを求めるのを推測するのではなく、LogRocketすぐに何が間違って理解するためにセッションをリプレイすることができます.これは、フレームワークに関係なく、任意のアプリケーションを完全に動作し、RedUx、Vuex、および@ NGRX/ストアからの追加のコンテキストを記録するプラグインがあります.

    ログのReduxのアクションと状態に加えて、ログログオンレコードコンソールログ、JavaScriptのエラー、StackTrart、ヘッダー/本文、ブラウザのメタデータ、およびカスタムログを使用してネットワークのリクエスト/応答.また、DOMは、最も複雑な単一ページのアプリのピクセル完璧なビデオを再現、ページ上のHTMLとCSSを記録するために楽器を計る.

    Try it for free .
    郵便Make CRUD simple with Node, GraphQL, and React 最初に現れたLogRocket Blog .