call突变_深入研究GraphQL突变

call突变

This is a continuation on the series i’ve been doing on GraphQL Actions. In the last post on this series we worked on a Deep Dive into Queries. This time, we will take a closer look at Mutations. There’s a lot of similarities in the way GraphQL Queries and Mutations work, however, Mutations is more about mutating data than querying for it. In this article, we’ll dive more into it and treat it’s concepts in details.

这是我在GraphQL Actions上所做的系列的延续。 本系列的最后一篇文章中 ,我们致力于深入研究查询。 这次,我们将仔细研究突变。 GraphQL查询和变异的工作方式有很多相似之处,但是,变异更多的是关于变异数据而不是查询数据。 在本文中,我们将更深入地研究它,并详细处理它的概念。

Mutation in any GraphQL application is very essential to the application’s data integrity. Once the mutation is done wrongly, the data output of such application will be wrong and if such data is being used for decision making, it could be catastrophic.

任何GraphQL应用程序中的突变对于应用程序的数据完整性都是至关重要的。 一旦错误地完成了突变,该应用程序的数据输出将是错误的,并且如果将此类数据用于决策,则可能是灾难性的。

That is why the mutation request object is not like the query request object which you do not need to prefix with a keyword before making query requests. The mutation prefix before the request is to make sure that request is being intentional and that whoever is making the request knows the consequences of his/her action.

这就是为什么变异请求对象与查询请求对象不同的原因,在发出查询请求之前,不需要为该对象添加关键字前缀。 请求之前的突变前缀是为了确保请求是有意的,并且提出请求的人都知道其行动的后果。

设计变异方案 ( Designing a Mutation schema )

Designing a schema for a mutation is quite similar to that of a query. In fact ones you’ve written a query schema, you should have no problem with designing a mutation schema. Let us look at the block of code below which shows us how to write a mutation schema.

为突变设计模式与查询非常相似。 实际上,您已经编写了查询模式,设计突变模式应该没有问题。 让我们看一下下面的代码块,它向我们展示了如何编写变异模式。

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]
      }
    },
  }
})

The schema above describes a mutation called addChef which adds the data about a chef to an array (since we are not actually creating a real application). The schema contains some key value pairs or properties that lets GraphQL know that we are trying to create a schema for a Mutation. One of them is thename property which we've set to the Mutation. Another one is the fields property which in turn has other two sub-properties which are addChef and the resolve function.

上面的模式描述了一个名为addChef的变addChef ,该变体将有关厨师的数据添加到数组中(因为我们实际上并未创建真正的应用程序)。 该模式包含一些键值对或属性,这些值或属性使GraphQL知道我们正在尝试为Mutation创建模式。 其中之一是我们设置为Mutation的name属性。 另一个是fields属性,该属性又具有另外两个子属性,分别是addChefresolve函数。

The addChef object contains information about the Mutation function. It could be named anything. This is the name that would be referred to when we try to mutate a data. The kwarg object contains the arguments that the function addChef will expect and their GraphQL data types.

addChef对象包含有关Mutation函数的信息。 它可以命名为任何东西。 这是我们尝试突变数据时要使用的名称。 该kwarg对象包含参数,函数addChef会期待和他们GraphQL数据类型。

Finally, we have the resolve function which is where all of the mutation action take place. It takes in two arguments in this instance. One is the parent argument which points to the data linked to the addChef query which we do not have in this instance. So, the parent argument isn’t quite useful here. The second argument is kwarg which refers to the arguments we have passed into the function.

最后,我们具有resolve函数,在该函数中所有突变动作都会发生。 在这种情况下,它接受两个参数。 一个是父参数,它指向链接到addChef查询的数据,在这种情况下我们没有此数据。 因此,父参数在这里不是很有用。 第二个参数是kwarg ,它指向我们传递给函数的参数。

At this point, we have created a mutation but it still won’t work yet. We need to find a way to let GraphQL know that what we’ve done here is a mutation. In order words, we need to find a way of registering the new instance we just created as a mutation and that’s quite easy to do. To register our mutation, we define it like so:

在这一点上,我们已经创建了一个变异,但是它仍然无法正常工作。 我们需要找到一种方法让GraphQL知道我们在这里所做的是一个突变。 换句话说,我们需要找到一种方法来注册我们刚刚创建的新实例作为变异,这很容易做到。 为了注册我们的突变,我们这样定义它:

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

创建变异 ( Creating a Mutation )

Having designed a mutation schema and registered it to the application, you can use the mutation addChef in this case, to add data ( a chef ) to the chefs array. If for instance, we want to create a new chef, we can run this mutation:

设计了一个变异模式并将其注册到应用程序后,在这种情况下,您可以使用变异addChef将数据(“ chef”)添加到chefs数组。 例如,如果我们要创建一个新的厨师,我们可以运行此突变:

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

What we’ve done here is use mutation to add data to the chefs array. You’ll also notice that we had to define the request inside a mutation object. This is so that GraphQL will recognize that we are not just trying to grab data from somewhere but we want to actually alter such data.

我们在这里所做的是使用变异将数据添加到chefs数组。 您还将注意到,我们必须在一个变异对象内定义请求。 这样GraphQL就会认识到我们不仅在尝试从某处获取数据,而且还想实际更改这些数据。

命名突变 ( Naming Mutations )

As much as we can only use the mutation keyword to carry out our mutation action, in production or in more robust applications, it will be better to name individual mutations for clarity and ease of use. That said, we could decide to be more descriptive and give a unique name the mutation we had in the previous example, this will be the result.

尽管我们只能使用mutation关键字来执行我们的突变操作,无论是在生产中还是在更强大的应用程序中,为了清楚和易于使用,更好地命名单个突变。 就是说,我们可以决定更具描述性,并给我们一个唯一的名称作为我们在上一个示例中具有的突变,这就是结果。

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

Now we can refer to this mutation with it’s unique name, mutateChef.

现在,我们可以使用唯一的名称mutateChef来引用此突变。

将查询变量传递给突变 ( Passing query variables to mutations )

Consider a situation where we want a user to update the data in our application by passing in new data, maybe from a text input field. This is usually the case in most applications, but at the moment that is not possible in our application since we passed static values directly into our query. What we can do is use variables to pass dynamic data into the query instead.

考虑一种情况,我们希望用户通过传入新数据(可能来自文本输入字段)来更新应用程序中的数据。 在大多数应用程序中通常是这种情况,但是目前在我们的应用程序中这是不可能的,因为我们将静态值直接传递到了查询中。 我们可以做的是使用变量将动态数据传递到查询中。

Thankfully, this is a very straightforward process in GraphQL. Let’s change the mutation query we’ve written previously to use variable inputs:

幸运的是,这是GraphQL中非常简单的过程。 让我们更改之前编写的使用变量输入的变异查询:

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

The snippet above is a good example of how we can modify our mutation query to use input variables instead of the previous static values. The variables are declared using the $ sign and then followed by the name we want to give the variable. Next we explicitly set a type for the variable. For example the $name variable was set to a String. The ! after the string shows that the field or variable is required.

上面的代码段很好地说明了如何修改变异查询以使用输入变量而不是先前的静态值。 变量使用$符号声明,然后加上我们要赋予变量的名称。 接下来,我们显式设置变量的类型。 例如, $name变量设置为String! 在字符串显示字段或变量是必需的之后。

Just like in the previous example, we can also give the mutation a unique name. The code snippet below shows a named mutation with dynamic values from variables.

就像在前面的示例中一样,我们还可以为突变指定一个唯一的名称。 下面的代码段显示了一个带有变量动态值的命名突变。

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

弄脏我们的手! ( Getting our hands dirty! )

Now that we’ve had fun understanding what Mutations are all about, It will be nice if we put the knowledge into good use and build a mini GraphQL app to give you a more practical experience.

既然我们已经很有趣地了解了突变的含义,那么如果我们充分利用所学的知识并构建一个微型GraphQL应用程序来为您提供更多实践经验,那将是很好的。

The application we'll build should be one that can allow us perform some basic CRUD operations on a bunch of mock data containing the names of some chefs. In clearer terms, the application will show you how to Create, Update, and Delete a particular chef from an array of chefs using the much talked about GraphQL mutation.

我们将构建的应用程序应该可以使我们对包含某些厨师姓名的一堆模拟数据执行一些基本的CRUD操作。 更明确地说,该应用程序将向您展示如何使用广受关注的GraphQL突变从一系列厨师中创建,更新和删除特定厨师。

设置开发服务器 (Set up a development server)

Before we dive into the app, we need to set up a server to send requests to. Let’s get started! First, initialize an empty project with npm init -y . Then proceed with installing all the necessary dependencies for the application:

在深入研究应用程序之前,我们需要设置服务器以将请求发送到。 让我们开始吧! 首先,使用npm init -y初始化一个空项目。 然后继续为应用程序安装所有必需的依赖项:

npm i express express-graphql app-root-path

Once the installation is complete, create a config directory then also created a port.js file in the directory and then update it with the code below:

安装完成后,创建一个config目录,然后在目录中创建一个port.js文件,然后使用以下代码对其进行更新:

// config/port.js

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

创建其他项目文件 (Create other project files)

In the root directory, create an app.js file that will serve as our entry file to the application. Then update it with the code below:

在根目录中,创建一个app.js文件,它将作为我们进入该应用程序的入口文件。 然后使用以下代码对其进行更新:

// app.js - entry file
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}`);
})

Finally go to your package.json file and create a dev script with the code below:

最后,转到您的package.json文件,并使用以下代码创建一个dev脚本:

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

This script command allows us to quickly start our application’s local server without typing so much terminal commands.

此脚本命令使我们能够快速启动应用程序的本地服务器,而无需键入太多终端命令。

创建应用程序逻辑和API ( Creating the app logic and API )

In the root directory, create a Server directory. Then also, create a model and a schema directory. In the model directory, create an index.js file and update it with the code below:

在根目录中,创建Server目录。 然后,还创建一个model和一个schema目录。 在model目录中,创建一个index.js文件,并使用以下代码对其进行更新:

// ./server/model/index.js

export const chefs = [
  {
    id: 1,
    name: "Monique Black"
  },
  {
    id: 2,
    name: "Chidinma Madukwe"
  } ]

Here, we are exporting an array of chefs containing the mock data we will use to represent the initial chefs we have in this application. Once that is done, create an index.js file in the schema directory and update it with the code below:

在这里,我们正在导出一个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
})

Yeah, that’s a lot of code but then It’s all what we’ve been discussing since the beginning of this tutorial. The only few things that were included but not previously covered are the GraphQL types which are:

是的,这是很多代码,但这就是自本教程开始以来我们一直在讨论的所有内容。 GraphQL类型仅包括但未涵盖的几项内容是:

  • GraphQLObjectType - Which is basically what we use to describe the structure of either a query or a mutation object.

    GraphQLObjectType-基本上就是我们用来描述查询或变异对象的结构的对象。

  • GraphQLNonNull - This tells GraphQL that once a file with a GraphQLNonNull type is missing in a query, such query or mutation won’t be made. The field becomes a required field.

    GraphQLNonNull-告诉GraphQL,一旦查询中缺少具有GraphQLNonNull类型的文件,就不会进行这种查询或变异。 该字段成为必填字段。

  • GraphQLID - This allows a query to be made if an id parameter is being passed in as either a string or an integer, and finally

    GraphQLID-如果以字符串或整数形式传入id参数,则最终可以进行查询

  • GraphQLList - This lets GraphQL know that the response is going to contain data of similar type in an array. GraphQL types aren’t just limited to these, the rest can be found on the GraphQL official website.

    GraphQLList-这使GraphQL知道响应将在数组中包含相似类型的数据。 GraphQL类型不仅限于这些类型,其余的可以在GraphQL官方网站上找到。

Finally, go back to the app.js file and update it with the code below:

最后,返回到app.js文件,并使用以下代码对其进行更新:

// app.js - entry file
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}`);
})

启动服务器 (Start the server)

After grabbing the code, run the npm run dev script command on your terminal to start the local server. You should get this message logged to the console when your server is running successfully.

抓取代码后,在终端上运行npm run dev script命令以启动本地服务器。 服务器成功运行时,应该将此消息记录到控制台。

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

在Graphiql上测试突变 ( Testing the Mutations on Graphiql )

Now that we have a server, let’s test our application with Graphiql. Graphiql is a GUI application that is shipped or embedded into the express-graphql module we installed earlier. To open it up, go to your browser and visit this URL http://localhost:9091/graphql. You should see a page similar to the one i have below:

现在我们有了服务器,让我们用Graphiql测试我们的应用程序。 Graphiql是一个GUI应用程序,已附带或嵌入到我们先前安装的express-graphql模块中。 要打开它,请转到浏览器并访问此URL http://localhost:9091/graphql 。 您应该会看到与以下页面相似的页面:

To create a query that will return all the chefs in our existing dataset, update the left hand side of the GUI with the snippet below:

要创建将返回现有数据集中所有chefs的查询,请使用以下代码段更新GUI的左侧:

{
  chefs {
    id,
    name
  }
}

Now click the Play button on the GUI and you should have the same output as the one below:

现在,单击GUI上的“ 播放”按钮,您应该具有与以下输出相同的输出:

As expected, it returns all the static data we currently have in our chefs array.

如预期的那样,它将返回我们的chefs数组中当前拥有的所有静态数据。

###

###

创建新厨师 (Create new chef)

To create a mutation that adds a new chef to the existing chefs dataset, we pass in a new name to the addChef function we defined in the schema. Here's an example code to add a new chef with the name "Chinwe Eze":

要创建将新厨师添加到现有厨师数据集中的突变,我们将新名称传递给在架构中定义的addChef函数。 这是添加名为“ Chinwe Eze”的新厨师的示例代码:

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

You can verify this functionality by running this snippet in the Graphiql GUI, you should get the same behavior as mine below:

您可以通过在Graphiql GUI中运行此代码段来验证此功能,您应获得与以下代码相同的行为:

If you’re wondering why we have an
id of 6 for the new chef, it is because we are generating random ID’s for every new chef. You can check back on the schema to verify this feature.

id6 ,那是因为我们为每个新厨师生成了随机ID。 您可以重新检查架构以验证此功能。

更新现有厨师 (Update existing chef)

If we wanted to update an existing data, we could redefine our mutation and use the updateChef function we defined in the schema to modify an existing data. A typical use case would be a user who wants to update their profile information.

如果要更新现有数据,则可以重新定义突变,并使用在模式中定义的updateChef函数来修改现有数据。 典型的用例是想要更新其个人资料信息的用户。

In our case, if we wanted to update the name of an existing chef, all we need to do is pass in the id of the chef with the new name to the updateChef function. Let’s update the chef with the name Monique Black to Simona White:

You’ll notice that this mutation is quite different from the rest because it takes in two arguments. However, a mutation function isn’t just limited to one or just two arguments, it can take as many arguments as you want. The only thing you should be wary about is whether you are passing in the right arguments.

在我们的例子中,如果我们想更新现有厨师的名称,我们要做的就是将具有新名称的厨师的id传递给updateChef函数。 让我们将名称Monique Black的厨师更新为Simona White

删除厨师 (Delete a chef)

Just like every other previous operation we’ve performed with our mutation, we can delete an existing data, in our case, we’ll delete a chef from our existing array of chefs. Again, we can pass in the id of a chef to the deleteChef function we defined in the schema and the chef will be removed.

就像我们对变异执行的所有其他先前操作一样,我们可以删除现有数据,在这种情况下,我们将从现有厨师集中删除一个厨师。 同样,我们可以将厨师的id传递给在架构中定义的deleteChef函数,然后将删除厨师。

结论 ( Conclusion )

In this post, we took an in-depth look at GraphQL mutations. We demonstrated it’s concepts like designing mutation schemas, naming and testing mutations, passing in query variables to our mutations and so on. We went further to create a mini GraphQL project to give you a more practical example on working with GraphQL mutation in real-world applications.

在这篇文章中,我们深入研究了GraphQL突变。 我们演示了它的概念,例如设计突变方案,命名和测试突变,将查询变量传递给我们的突变等。 我们进一步创建了一个迷你GraphQL项目,为您提供了在实际应用中使用GraphQL突变的更实际的示例。

Summarily, Mutation provides us a very flexible way to perform the CUD in CRUD (Create, Read, Update, Delete) without really going through complex data queries or programming logic. Once a schema has been created, and a relationship exists between related data, then there are no much programming to be done to get the needed data. In my opinion, GraphQL generally make API design more interesting and less complex for us developers.

综上所述,突变为我们提供了一种非常灵活的方式来执行CRUD(创建,读取,更新,删除)中的CUD,而无需真正地进行复杂的数据查询或编程逻辑。 一旦创建了架构,并且相关数据之间存在关系,那么就不需要进行太多的编程来获取所需的数据。 在我看来,GraphQL通常使API开发对于我们的开发人员来说更加有趣,并且更加简单。

翻译自: https://scotch.io/tutorials/deep-dive-into-graphql-mutations

call突变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值