開発者ドキュメント

GraphQLのミューテーションを理解する

これは、GraphQLアクションに関するシリーズの続きです。 前のチュートリアルでは、GraphQLクエリについて説明しました。 今回は、Mutationsについて詳しく見ていきます。 GraphQLクエリとミューテーションの動作には多くの類似点がありますが、ミューテーションはデータのクエリよりもデータのミューテーションに関するものです。 この記事では、さらに詳しく説明し、その概念を詳しく説明します。

GraphQLアプリケーションでの変更は、アプリケーションのデータ整合性にとって非常に重要です。 ミューテーションが正しく行われないと、そのようなアプリケーションのデータ出力は間違ってしまい、そのようなデータが意思決定に使用されている場合、それは壊滅的である可能性があります。

そのため、ミューテーションリクエストオブジェクトは、クエリリクエストを行う前にキーワードをプレフィックスとして付ける必要のないクエリリクエストオブジェクトとは異なります。 リクエストの前のミューテーションプレフィックスは、リクエストが意図的なものであり、リクエストを行っている人が自分のアクションの結果を知っていることを確認するためのものです。

ミューテーションスキーマの設計

ミューテーションのスキーマの設計は、クエリのスキーマと非常によく似ています。 ミューテーションスキーマの記述方法を示す以下のコードブロックを見てみましょう。

let chefs = []
const Mutations = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    addChef: {
      type: ChefType,
      kwarg: {
        name: { type: GraphQLString },
        age: { type: GraphQLInt},
        hobby: { type: GraphQLInt},
      },
      resolve(parent, kwarg) {
        kwarg.id = Math.random()
        return [...chefs, kwarg]
      }
    },
  }
})

スキーマは、シェフに関するデータを配列に追加するaddChefと呼ばれるミューテーションを記述します(実際には実際のアプリケーションを作成していないため)。 スキーマには、Mutationのスキーマを作成しようとしていることをGraphQLに通知するいくつかのキー/値のペアまたはプロパティが含まれています。 それらの1つは、Mutationに設定したnameプロパティです。 もう1つは、fieldsプロパティで、addChef関数とresolve関数の2つのサブプロパティがあります。

addChefオブジェクトには、Mutation関数に関する情報が含まれています。 それは何でも名前を付けることができます。 これは、データを変更しようとするときに参照される名前です。 kwargオブジェクトには、関数addChefが期待する引数とそのGraphQLデータ型が含まれています。

最後に、すべてのミューテーションアクションが実行されるresolve関数があります。 この場合、2つの引数を取ります。 1つは、addChefクエリにリンクされたデータを指す親引数です。この場合、この引数はありません。 したがって、ここでは親引数はあまり役に立ちません。 2番目の引数はkwargで、これは関数に渡した引数を参照します。

この時点で、ミューテーションを作成しましたが、まだ機能しません。 ここで行ったことは突然変異であることをGraphQLに知らせる方法を見つける必要があります。 つまり、作成したばかりの新しいインスタンスをミューテーションとして登録する方法を見つける必要があります。 ミューテーションを登録するには、次のように定義します。

module.exports = new GraphQLSchema({
  mutation: Mutations
})

ミューテーションの作成

ミューテーションスキーマを設計してアプリケーションに登録したら、この場合はミューテーションaddChefを使用して、データ(chef)をchefs配列に追加できます。 たとえば、新しいシェフを作成したい場合は、次のミューテーションを実行できます。

mutation {
  addChef(name: "Swae Yu", age: "30", hobby:"Swimming"){
    id,
    age,
    name,
    hobby
  }
}

ここで行ったことは、ミューテーションを使用してchefs配列にデータを追加することです。 また、ミューテーションオブジェクト内でリクエストを定義する必要があることにも気付くでしょう。 これは、GraphQLが、どこかからデータを取得しようとしているだけでなく、そのようなデータを変更したいことを認識できるようにするためです。

突然変異の命名

mutationキーワードを使用して、本番環境またはより堅牢なアプリケーションでミューテーションアクションを実行することしかできませんが、わかりやすく使いやすいように、個々のミューテーションに名前を付けることをお勧めします。 そうは言っても、前の例で持っていたミューテーションに、より説明的で一意の名前を付けることを決定できます。これが結果になります。

    mutation mutateChef {
      addChef(name: "Swae Yu", age: "30", hobby:"Swimming"){
        id,
        age,
        name,
        hobby
      }
    }

これで、このミューテーションを一意の名前mutateChefで参照できます。

クエリ変数をミューテーションに渡す

テキスト入力フィールドなどから新しいデータを渡すことで、ユーザーがアプリケーションのデータを更新できるようにする状況を考えてみます。 これは通常、ほとんどのアプリケーションに当てはまりますが、静的な値をクエリに直接渡したため、現時点ではアプリケーションでは不可能です。 代わりに、変数を使用して動的データをクエリに渡すことができます。

ありがたいことに、これはGraphQLの非常に単純なプロセスです。 以前に作成したミューテーションクエリを変更して、変数入力を使用してみましょう。

    mutation ($name: String!, $age: Int!, $hobby: String!){
      addChef(name: $name, age: $age, hobby:$hobby){
        id,
        age,
        name,
        hobby
      }
    }

上記のスニペットは、以前の静的値の代わりに入力変数を使用するようにミューテーションクエリを変更する方法の良い例です。 変数は$記号を使用して宣言され、その後に変数に付ける名前が続きます。 次に、変数の型を明示的に設定します。 たとえば、$name変数はStringに設定されました。 文字列の後の!は、フィールドまたは変数が必須であることを示しています。

前の例と同様に、ミューテーションに一意の名前を付けることもできます。 以下のコードスニペットは、変数からの動的な値を持つ名前付きミューテーションを示しています。

mutation mutateChef($name: String!, $age: Int!, $hobby: String!){
  addChef(name: $name, age: $age, hobby:$hobby){
    id,
    age,
    name,
    hobby
  }
}

アプリの構築

Mutationsとは何かを理解したので、ミニGraphQLアプリを作成して、より実用的なエクスペリエンスを提供できます。

構築するアプリケーションは、シェフの名前を含む一連のモックデータに対して基本的なCRUD操作を実行できるアプリケーションである必要があります。 より明確に言えば、アプリケーションは、話題のGraphQL Mutationを使用して、シェフの配列から特定のシェフを作成、更新、および削除する方法を示します。

開発サーバーのセットアップ

アプリに飛び込む前に、リクエストを送信するサーバーを設定する必要があります。

まず、空のプロジェクトをnpm init -yで初期化します。 次に、アプリケーションに必要なすべての依存関係のインストールに進みます。

  1. npm i express express-graphql app-root-path

インストールが完了したら、configディレクトリを作成し、そのディレクトリにport.jsファイルを作成して、次のコードで更新します。

config / port.js

export const PORT = process.env.PORT || 9091

他のプロジェクトファイルの作成

ルートディレクトリに、アプリケーションへのエントリファイルとして機能するapp.jsファイルを作成します。 次に、以下のコードで更新します。

app.js
import express from 'express';
import graphqlHTTP from 'express-graphql';
import { PORT } from './config/port';
import logger from './config/winston';

const app = express();

app.use(cors())
app.use('/graphql', graphqlHTTP({
   graphiql: true
}))

app.listen(PORT, () => {
  logger.info(`Server now listening for request at port ${PORT}`);
})

最後に、package.jsonファイルに移動し、次のコードを使用してdevスクリプトを作成します。

package.json
"dev": "nodemon --exec babel-node app.js"

このスクリプトコマンドを使用すると、多くのターミナルコマンドを入力しなくても、アプリケーションのローカルサーバーをすばやく起動できます。

アプリロジックとAPIの作成

ルートディレクトリに、Serverディレクトリを作成します。 次に、modelおよびschemaディレクトリを作成します。 modelディレクトリに、index.jsファイルを作成し、以下のコードで更新します。

/server/model/index.js
export const chefs = [
  {
    id: 1,
    name: "Monique Black"
  },
  {
    id: 2,
    name: "Chidinma Madukwe"
  } ]

ここでは、このアプリケーションで最初のシェフを表すために使用するモックデータを含むchefsの配列をエクスポートしています。 それが完了したら、schemaディレクトリにindex.jsファイルを作成し、以下のコードで更新します。

./server/schema/index.js
import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLID,
  GraphQLList,
  GraphQLSchema,
  GraphQLNonNull
  } from 'graphql'
import { chefs } from '../model/'

const ChefType = new GraphQLObjectType({
  name: 'chef',
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString}
  })
})

const RootQuery = new GraphQLObjectType({
  name: 'RootQuery',
  fields: {
    chefs: {
      type: new GraphQLList(ChefType),
      resolve() {
        return chefs
      }
    }
  }
})

const Mutations = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    deleteChef: {
      type: ChefType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLID)}
      },
      resolve(parent, args) {
        const returnedData = chefs.filter(chef => {
          return chef.id == args.id
        })
        return returnedData[0]
      }
    },
    addChef: {
      type: new GraphQLList(ChefType),
      args: {
        name: {type: new GraphQLNonNull(GraphQLString)}
      },
      resolve(parent, args) {
        args.id = Math.floor(Math.random() * 10)
        return [...chefs, args]
      }
    },
    updateChef: {
      type: ChefType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLID)},
        name: { type: new GraphQLNonNull(GraphQLString)}
      },
      resolve(parent, args) {
        const updateChef = chefs.find(data => {
          return data.id == args.id
        })
        updateChef.name = args.name
        return updateChef
      }
    }
  }
})

export const schema = new GraphQLSchema({
    query: RootQuery,
    mutation: Mutations
})

これは長いコードファイルですが、チュートリアルで触れたすべてのものが含まれています。 含まれているが以前はカバーされていなかったものは、次のようなGraphQLタイプだけです。

最後に、app.jsファイルに戻り、以下のコードで更新します。

app.js
import express from 'express';
import graphqlHTTP from 'express-graphql';
import cors from 'cors';

import { PORT } from './config/port';
import logger from './config/winston';
import {schema} from './server/schema/';

const app = express();

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

app.listen(PORT, () => {
  logger.info(`Server now listening for request at port ${PORT}`);
})

サーバーの起動

コードを取得したら、端末でnpm run devスクリプトコマンドを実行して、ローカルサーバーを起動します。 サーバーが正常に実行されている場合は、このメッセージがコンソールに記録されるはずです。

"message":"Server now listening for request at port 9090","level":"info"}

Graphiqlでのミューテーションのテスト

サーバーができたので、Graphiqlを使用してアプリケーションをテストしてみましょう。 Graphiqlは、以前にインストールしたexpress-graphqlモジュールに同梱または組み込まれているGUIアプリケーションです。 開くには、ブラウザにアクセスして、このURLhttp://localhost:9091/graphqlにアクセスしてください。 以下のようなページが表示されます。

既存のデータセット内のすべてのchefsを返すクエリを作成するには、GUIの左側を以下のスニペットで更新します。

{
  chefs {
    id,
    name
  }
}

GUIのPlayボタンをクリックすると、以下のような出力が得られます。

予想どおり、chefs配列に現在あるすべての静的データが返されます。

新しいシェフの作成

既存のchefsデータセットに新しいchefを追加するミューテーションを作成するには、スキーマで定義したaddChef関数に新しい名前を渡します。 「ChinweEze」という名前の新しいシェフを追加するコード例を次に示します。

mutation{
  addChef(name: "Chinwe Eze"){
    id,
    name
  }
}

Graphiql GUIでこのスニペットを実行することで、この機能を確認できます。以下の私のものと同じ動作が得られるはずです。

新しいシェフのid6である理由がわからない場合は、新しいシェフごとにランダムなIDを生成しているためです。 スキーマを再度確認して、この機能を確認できます。

既存のシェフの更新

既存のデータを更新したい場合は、ミューテーションを再定義し、スキーマで定義したupdateChef関数を使用して既存のデータを変更できます。 典型的な使用例は、プロファイル情報を更新したいユーザーです。

この場合、既存のシェフの名前を更新する場合は、シェフのidを新しい名前でupdateChef関数に渡すだけです。 Monique Blackという名前のシェフをSimona Whiteに更新しましょう。

このミューテーションは2つの引数を取るため、他のミューテーションとはかなり異なることに気付くでしょう。 ただし、ミューテーション関数は1つまたは2つの引数に限定されるのではなく、必要な数の引数を取ることができます。 あなたが注意しなければならない唯一のことは、あなたが正しい議論を渡しているかどうかです。

シェフの削除

ミューテーションで実行した他のすべての以前の操作と同様に、既存のデータを削除できます。この場合、既存のシェフの配列からシェフを削除します。 ここでも、シェフのidをスキーマで定義したdeleteChef関数に渡すことができ、シェフは削除されます。

結論

この投稿では、GraphQLミューテーションについて詳しく見てきました。 ミューテーションスキーマの設計、ミューテーションの命名とテスト、ミューテーションへのクエリ変数の受け渡しなどの概念を示しました。 さらに、ミニGraphQLプロジェクトを作成して、実際のアプリケーションでGraphQLミューテーションを操作する際のより実用的な例を示しました。

結論として、Mutationsは、複雑なデータクエリやプログラミングロジックを実際に実行することなく、CRUD(作成、読み取り、更新、削除)でCUDを実行するための非常に柔軟な方法を提供します。 スキーマが作成され、関連データ間に関係が存在する場合、必要なデータを取得するために実行するプログラミングはそれほど多くありません。

モバイルバージョンを終了