为什么 GraphQL 查询返回 null? [英] Why does a GraphQL query return null?

查看:75
本文介绍了为什么 GraphQL 查询返回 null?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 graphql/apollo-server/graphql-yoga 端点.此端点公开从数据库(或 REST 端点或某些其他服务)返回的数据.

我知道我的数据源正在返回正确的数据——如果我在解析器中记录对数据源的调用结果,我可以看到正在返回的数据.但是,我的 GraphQL 字段总是解析为 null.

如果我将该字段设为非空,我会在响应中的 errors 数组中看到以下错误:

<块引用>

不能为非空字段返回空

为什么 GraphQL 不返回数据?

解决方案

您的一个或多个字段解析为 null 的常见原因有两个:1) 在解析器中以错误的形状返回数据;和 2) 没有正确使用 Promise.

注意:如果您看到以下错误:

<块引用>

不能为非空字段返回空

根本问题是您的字段返回空值.您仍然可以按照下面列出的步骤尝试解决此错误.

以下示例将引用这个简单的架构:

类型查询{帖子(ID:ID):帖子帖子:[帖子]}输入帖子{身份证:身份证标题:字符串正文:字符串}

以错误的形式返回数据

我们的架构以及请求的查询定义了端点返回的响应中 data 对象的形状".形状是指对象具有的属性,以及这些属性的值是标量值、其他对象还是对象数组或标量.

与模式定义总响应的形状相同,单个字段的类型定义了该字段值的形状.我们在解析器中返回的数据的形状也必须与这个预期的形状相匹配.如果没有,我们的响应中经常会出现意外的空值.

在我们深入研究具体示例之前,了解 GraphQL 如何解析字段非常重要.

了解默认解析器行为

虽然您当然可以为架构中的每个字段编写一个解析器,但通常没有必要,因为 GraphQL.js 在您不提供时使用默认解析器.

在高层次上,默认解析器的作用很简单:它查看 parent 字段解析到的值,如果该值是 JavaScript 对象,它会查找该对象上的属性与正在解析的字段同名.如果找到该属性,则解析为该属性的值.否则,它解析为 null.

假设在 post 字段的解析器中,我们返回值 { title: 'My First Post', bod: 'Hello World!'}.如果我们不为 Post 类型的任何字段编写解析器,我们仍然可以请求 post:

查询{邮政 {ID标题身体}}

我们的回应是

<代码>{数据": {邮政" {id":空,"title": "我的第一篇文章",身体":空,}}}

title 字段被解析,即使我们没有为它提供解析器,因为默认解析器做了繁重的工作——它看到有一个名为 title 在 Object 上,父字段(在本例中为 post)解析为,因此它只是解析为该属性的值.id 字段解析为 null,因为我们在 post 解析器中返回的对象没有 id 属性.由于输入错误,body 字段也解析为 null -- 我们有一个名为 bod 的属性,而不是 body

<块引用>

专业提示:如果bod不是打字错误而是API或数据库实际返回的内容,我们总是可以为body 字段来匹配我们的架构.例如:(parent) =>parent.bod

要记住的一件重要事情是,在 JavaScript 中,几乎所有东西都是对象.因此,如果 post 字段解析为字符串或数字,Post 类型上每个字段的默认解析器仍将尝试在父对象,不可避免地失败并返回 null.如果某个字段具有对象类型,但您在其解析器中返回对象以外的内容(如字符串或数组),您将不会看到任何有关类型不匹配的错误,但该字段的子字段将不可避免地解析为 null.

常见场景 #1:包装响应

如果我们正在为 post 查询编写解析器,我们可能会从其他端点获取我们的代码,如下所示:

function post (root, args) {//轴return axios.get(`http://SOME_URL/posts/${args.id}`).then(res => res.data);//拿来返回获取(`http://SOME_URL/posts/${args.id}`).then(res => res.json());//请求-承诺-本地返回请求({uri:`http://SOME_URL/posts/${args.id}`,json: 真});}

post 字段的类型为 Post,所以我们的解析器应该返回一个对象,其属性类似于 id, titlebody.如果这是我们的 API 返回的内容,那么我们都准备好了.然而,响应实际上是一个包含额外元数据的对象是很常见的.所以我们实际从端点返回的对象可能看起来像这样:

<代码>{状态":200,结果": {身份证":1,"title": "我的第一篇文章",身体":世界,你好!"},}

在这种情况下,我们不能只是按原样返回响应并期望默认解析器正常工作,因为我们返回的对象没有 id , title 和 body 属性.我们的解析器不需要执行以下操作:

function post (root, args) {//轴return axios.get(`http://SOME_URL/posts/${args.id}`).then(res => res.data.result);//拿来返回获取(`http://SOME_URL/posts/${args.id}`).then(res => res.json()).then(data => data.result);//请求-承诺-本地返回请求({uri:`http://SOME_URL/posts/${args.id}`,json: 真}).then(res => res.result);}

注意:以上示例从另一个端点获取数据;然而,当直接使用数据库驱动程序(而不是使用 ORM)时,这种包装响应也很常见!例如,如果您使用 node-postgres,您将获得 Result包含诸如 rowsfieldsrowCountcommand 等属性的对象.在将其返回到解析器中之前,您需要从该响应中提取适当的数据.

常见场景 #2:数组而不是对象

如果我们从数据库中获取一篇文章,我们的解析器可能看起来像这样:

function post(root, args, context) {返回 context.Post.find({ where: { id: args.id } })}

其中 Post 是我们通过上下文注入的一些模型.如果我们使用 sequelize,我们可能会调用 findAll.mongoosetypeormfind.这些方法的共同点是,虽然它们允许我们指定 WHERE 条件,但它们返回的 Promise 仍然解析为数组而不是单个对象.虽然您的数据库中可能只有一个具有特定 ID 的帖子,但当您调用这些方法之一时,它仍然包含在一个数组中.因为数组仍然是对象,GraphQL 不会将 post 字段解析为 null.但它解析所有子字段为空,因为它将无法在数组中找到适当命名的属性.

您只需抓取数组中的第一项并将其返回到解析器中即可轻松解决此问题:

function post(root, args, context) {返回 context.Post.find({ where: { id: args.id } }).then(posts =>posts[0])}

如果您从另一个 API 获取数据,这通常是唯一的选择.另一方面,如果您使用的是 ORM,则通常可以使用不同的方法(例如 findOne),该方法将仅显式返回 DB 中的一行(如果没有,则返回 null不存在).

function post(root, args, context) {返回 context.Post.findOne({ where: { id: args.id } })}

关于INSERTUPDATE 调用的特别说明:我们经常期望插入或更新行或模型实例的方法返回插入的或更新的行.通常他们会这样做,但有些方法不会.例如,sequelizeupsert 方法解析为一个布尔值,或更新插入记录的元组和一个布尔值(如果 returning 选项是设置为真).mongoosefindOneAndUpdate 解析为具有 value 属性的对象,该对象包含修改后的行.在将结果返回到解析器之前,请查阅您的 ORM 文档并适当地解析结果.

常见场景 #3:对象而不是数组

在我们的模式中,posts 字段的类型是 PostList,这意味着它的解析器需要返回一个对象数组(或解析为 1 的 Promise).我们可能会像这样获取帖子:

函数帖子(根,参数){return fetch('http://SOME_URL/posts').then(res => res.json())}

然而,我们 API 的实际响应可能是一个包装帖子数组的对象:

<代码>{计数":10,"next": "http://SOME_URL/posts/?page=2",上一个":空,结果": [{身份证":1,"title": "我的第一篇文章",身体"世界你好!"},...]}

我们不能在解析器中返回这个对象,因为 GraphQL 需要一个数组.如果我们这样做,该字段将解析为 null,我们将在响应中看到一个错误,例如:

<块引用>

预期可迭代,但未找到字段 Query.posts.

与上述两种情况不同,在这种情况下,GraphQL 能够显式检查我们在解析器中返回的值的类型,如果它不是 Iterable 就像一个数组.

就像我们在第一个场景中讨论的那样,为了修复这个错误,我们必须将响应转换为适当的形状,例如:

函数帖子(根,参数){return fetch('http://SOME_URL/posts').then(res => res.json()).then(data => data.results)}

没有正确使用 Promise

GraphQL.js 在底层使用了 Promise API.因此,解析器可以返回一些值(例如 { id: 1, title: 'Hello!' }),或者它可以返回一个 Promise 来解析到那个值.对于具有 List 类型的字段,您还可以返回一个 Promise 数组.如果 Promise 拒绝,该字段将返回 null,并且相应的错误将添加到响应中的 errors 数组中.如果字段具有 Object 类型,则 Promise 解析的值将作为父值传递给任何子字段的解析器.

Promise 是一个对象表示异步操作的最终完成(或失败)及其结果值."接下来的几个场景概述了在解析器中处理 Promise 时遇到的一些常见陷阱.但是,如果您不熟悉 Promise 和较新的 async/await 语法,强烈建议您花一些时间阅读基础知识.

注意:接下来的几个例子指的是一个 getPost 函数.这个函数的实现细节并不重要——它只是一个返回 Promise 的函数,它将解析为一个 post 对象.

常见场景 #4:不返回值

post 字段的工作解析器可能如下所示:

function post(root, args) {返回 getPost(args.id)}

getPosts 返回一个 Promise,我们正在返回那个 Promise.Promise 解析为的任何值都将成为我们的字段解析为的值.看起来不错!

但是如果我们这样做会发生什么:

function post(root, args) {getPost(args.id)}

我们仍在创建将解析为帖子的 Promise.但是,我们没有返回 Promise,因此 GraphQL 不知道它并且不会等待它解决.在没有显式 return 语句的 JavaScript 函数中,隐式返回 undefined.所以我们的函数创建了一个 Promise,然后立即返回 undefined,导致 GraphQL 为该字段返回 null.

如果 getPost 返回的 Promise 拒绝,我们也不会在响应中看到任何错误——因为我们没有返回 Promise,底层代码不关心是否它解决或拒绝.事实上,如果 Promise 拒绝,你会看到一个 UnhandledPromiseRejectionWarning 在您的服务器控制台中.

解决这个问题很简单——只需添加return.

常见场景 #5:未正确链接 Promise

您决定记录对 getPost 的调用结果,因此您将解析器更改为如下所示:

function post(root, args) {返回 getPost(args.id).then(post => {控制台日志(后)})}

当您运行查询时,您会看到结果记录在您的控制台中,但 GraphQL 将该字段解析为 null.为什么?

当我们对 Promise 调用 then 时,我们实际上是在获取 Promise 解析的值并返回一个新的 Promise.除了 Promises 之外,你可以把它想象成类似于 Array.map.then 可以返回一个值,或者另一个 Promise.在任何一种情况下, then 内部返回的内容都链接"到原始 Promise 上.多个 Promise 可以通过使用多个 then 像这样链接在一起.链中的每个 Promise 都会依次解析,最终的值就是有效解析为原始 Promise 的值.

在我们上面的例子中,我们在 then 内部没有返回任何内容,因此 Promise 解析为 undefined,GraphQL 将其转换为 null.为了解决这个问题,我们必须返回帖子:

function post(root, args) {返回 getPost(args.id).then(post => {控制台日志(后)回帖//<----})}

如果您有多个 Promise 需要在解析器中解析,则必须使用 then 正确链接它们并返回正确的值.例如,如果我们在调用getPost之前需要调用另外两个异步函数(getFoogetBar),我们可以这样做:>

function post(root, args) {返回 getFoo().then(foo => {//用 foo 做一些事情return getBar()//返回链中的下一个 Promise}).then(bar => {//用 bar 做一些事情return getPost(args.id)//返回链中的下一个 Promise})

<块引用>

专业提示:如果您正在努力正确链接 Promise,您可能会发现 async/await 语法更简洁、更易于使用.

常见场景 #6

在 Promises 之前,处理异步代码的标准方法是使用回调或异步工作完成后将调用的函数.例如,我们可以像这样调用 mongoosefindOne 方法:

function post(root, args) {return Post.findOne({ where: { id: args.id } }, function (err, post) {回帖})

这里的问题有两个方面.一,在回调中返回的值不用于任何事情(即它不会以任何方式传递给底层代码).二、当我们使用回调时,Post.findOne 不会返回一个 Promise;它只是返回未定义.在这个例子中,我们的回调将被调用,如果我们记录 post 的值,我们将看到从数据库返回的任何内容.然而,因为我们没有使用 Promise,GraphQL 不会等待这个回调完成——它接受返回值(未定义)并使用它.

大多数更流行的库,包括 mongoose 都支持开箱即用的 Promise.那些不经常拥有添加此功能的免费包装器"库.在使用 GraphQL 解析器时,您应该避免使用利用回调的方法,而应使用返回 Promise 的方法.

<块引用>

专业提示:同时支持回调和承诺的库经常重载它们的函数,如果没有提供回调,函数将返回一个承诺.有关详细信息,请查看图书馆的文档.

如果你绝对必须使用回调,你也可以将回调包装在一个 Promise 中:

function post(root, args) {返回新的承诺((解决,拒绝)=> {Post.findOne({ where: { id: args.id } }, function (err, post) {如果(错误){拒绝(错误)} 别的 {解决(发布)}})})

I have an graphql/apollo-server/graphql-yoga endpoint. This endpoint exposes data returned from a database (or a REST endpoint or some other service).

I know my data source is returning the correct data -- if I log the result of the call to the data source inside my resolver, I can see the data being returned. However, my GraphQL field(s) always resolve to null.

If I make the field non-null, I see the following error inside the errors array in the response:

Cannot return null for non-nullable field

Why is GraphQL not returning the data?

解决方案

There's two common reasons your field or fields are resolving to null: 1) returning data in the wrong shape inside your resolver; and 2) not using Promises correctly.

Note: if you're seeing the following error:

Cannot return null for non-nullable field

the underlying issue is that your field is returning null. You can still follow the steps outlined below to try to resolve this error.

The following examples will refer to this simple schema:

type Query {
  post(id: ID): Post
  posts: [Post]
}

type Post {
  id: ID
  title: String
  body: String
}

Returning data in the wrong shape

Our schema, along with the requested query, defines the "shape" of the data object in the response returned by our endpoint. By shape, we mean what properties objects have, and whether those properties' values' are scalar values, other objects, or arrays of objects or scalars.

In the same way a schema defines the shape of the total response, the type of an individual field defines the shape of that field's value. The shape of the data we return in our resolver must likewise match this expected shape. When it doesn't, we frequently end up with unexpected nulls in our response.

Before we dive into specific examples, though, it's important to grasp how GraphQL resolves fields.

Understanding default resolver behavior

While you certainly can write a resolver for every field in your schema, it's often not necessary because GraphQL.js uses a default resolver when you don't provide one.

At a high level, what the default resolver does is simple: it looks at the value the parent field resolved to and if that value is a JavaScript object, it looks for a property on that Object with the same name as the field being resolved. If it finds that property, it resolves to the value of that property. Otherwise, it resolves to null.

Let's say in our resolver for the post field, we return the value { title: 'My First Post', bod: 'Hello World!' }. If we don't write resolvers for any of the fields on the Post type, we can still request the post:

query {
  post {
    id
    title
    body
  }
}

and our response will be

{
  "data": {
    "post" {
      "id": null,
      "title": "My First Post",
      "body": null,
    }
  }
}

The title field was resolved even though we didn't provide a resolver for it because the default resolver did the heavy lifting -- it saw there was a property named title on the Object the parent field (in this case post) resolved to and so it just resolved to that property's value. The id field resolved to null because the object we returned in our post resolver did not have an id property. The body field also resolved to null because of a typo -- we have a property called bod instead of body!

Pro tip: If bod is not a typo but what an API or database actually returns, we can always write a resolver for the body field to match our schema. For example: (parent) => parent.bod

One important thing to keep in mind is that in JavaScript, almost everything is an Object. So if the post field resolves to a String or a Number, the default resolver for each of the fields on the Post type will still try to find an appropriately named property on the parent object, inevitably fail and return null. If a field has an object type but you return something other than object in its resolver (like a String or an Array), you will not see any error about the type mismatch but the child fields for that field will inevitably resolve to null.

Common Scenario #1: Wrapped Responses

If we're writing the resolver for the post query, we might fetch our code from some other endpoint, like this:

function post (root, args) {
  // axios
  return axios.get(`http://SOME_URL/posts/${args.id}`)
    .then(res => res.data);

  // fetch
  return fetch(`http://SOME_URL/posts/${args.id}`)
    .then(res => res.json());

  // request-promise-native
  return request({
    uri: `http://SOME_URL/posts/${args.id}`,
    json: true
  });
}

The post field has the type Post, so our resolver should return an object with properties like id, title and body. If this is what our API returns, we're all set. However, it's common for the response to actually be an object which contains additional metadata. So the object we actually get back from the endpoint might look something like this:

{
  "status": 200,
  "result": {
    "id": 1,
    "title": "My First Post",
    "body": "Hello world!"
  },
}

In this case, we can't just return the response as-is and expect the default resolver to work correctly, since the object we're returning doesn't have the id , title and body properties we need. Our resolver isn't needs to do something like:

function post (root, args) {
  // axios
  return axios.get(`http://SOME_URL/posts/${args.id}`)
    .then(res => res.data.result);

  // fetch
  return fetch(`http://SOME_URL/posts/${args.id}`)
    .then(res => res.json())
    .then(data => data.result);

  // request-promise-native
  return request({
    uri: `http://SOME_URL/posts/${args.id}`,
    json: true
  })
    .then(res => res.result);
}

Note: The above example fetches data from another endpoint; however, this sort of wrapped response is also very common when using a database driver directly (as opposed to using an ORM)! For example, if you're using node-postgres, you'll get a Result object that includes properties like rows, fields, rowCount and command. You'll need to extract the appropriate data from this response before returning it inside your resolver.

Common Scenario #2: Array Instead of Object

What if we fetch a post from the database, our resolver might look something like this:

function post(root, args, context) {
  return context.Post.find({ where: { id: args.id } })
}

where Post is some model we're injecting through the context. If we're using sequelize, we might call findAll. mongoose and typeorm have find. What these methods have in common is that while they allow us to specify a WHERE condition, the Promises they return still resolve to an array instead of a single object. While there's probably only one post in your database with a particular ID, it's still wrapped in an array when you call one of these methods. Because an Array is still an Object, GraphQL will not resolve the post field as null. But it will resolve all of the child fields as null because it won't be able to find the appropriately named properties on the array.

You can easily fix this scenario by just grabbing the first item in the array and returning that in your resolver:

function post(root, args, context) {
  return context.Post.find({ where: { id: args.id } })
    .then(posts => posts[0])
}

If you're fetching data from another API, this is frequently the only option. On the other hand, if you're using an ORM, there's often a different method that you can use (like findOne) that will explicitly return only a single row from the DB (or null if it doesn't exist).

function post(root, args, context) {
  return context.Post.findOne({ where: { id: args.id } })
}

A special note on INSERT and UPDATE calls: We often expect methods that insert or update a row or model instance to return the inserted or updated row. Often they do, but some methods don't. For example, sequelize's upsert method resolves to a boolean, or tuple of the the upserted record and a boolean (if the returning option is set to true). mongoose's findOneAndUpdate resolves to an object with a value property that contains the modified row. Consult your ORM's documentation and parse the result appropriately before returning it inside your resolver.

Common Scenario #3: Object Instead of Array

In our schema, the posts field's type is a List of Posts, which means its resolver needs to return an Array of objects (or a Promise that resolves to one). We might fetch the posts like this:

function posts (root, args) {
  return fetch('http://SOME_URL/posts')
    .then(res => res.json())
}

However, the actual response from our API might be an object that wraps the the array of posts:

{
  "count": 10,
  "next": "http://SOME_URL/posts/?page=2",
  "previous": null,
  "results": [
    {
      "id": 1,
      "title": "My First Post",
      "body" "Hello World!"
    },
    ...
  ]
}

We can't return this object in our resolver because GraphQL is expecting an Array. If we do, the field will resolve to null and we'll see an error included in our response like:

Expected Iterable, but did not find one for field Query.posts.

Unlike the two scenarios above, in this case GraphQL is able to explicitly check the type of the value we return in our resolver and will throw if it's not an Iterable like an Array.

Like we discussed in the first scenario, in order to fix this error, we have to transform the response into the appropriate shape, for example:

function posts (root, args) {
  return fetch('http://SOME_URL/posts')
    .then(res => res.json())
    .then(data => data.results)
}

Not Using Promises Correctly

GraphQL.js makes use of the Promise API under the hood. As such, a resolver can return some value (like { id: 1, title: 'Hello!' }) or it can return a Promise that will resolve to that value. For fields that have a List type, you may also return an array of Promises. If a Promise rejects, that field will return null and the appropriate error will be added to the errors array in the response. If a field has an Object type, the value the Promise resolves to is what will be passed down as the parent value to the resolvers of any child fields.

A Promise is an "object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value." The next few scenarios outline some common pitfalls encountered when dealing with Promises inside resolvers. However, if you're not familiar with Promises and the newer async/await syntax, it's highly recommended you spend some time reading up on the fundamentals.

Note: the next few examples refer to a getPost function. The implementation details of this function are not important -- it's just a function that returns a Promise, which will resolve to a post object.

Common Scenario #4: Not Returning a Value

A working resolver for the post field might looks like this:

function post(root, args) {
  return getPost(args.id)
}

getPosts returns a Promise and we're returning that Promise. Whatever that Promise resolves to will become the value our field resolves to. Looking good!

But what happens if we do this:

function post(root, args) {
  getPost(args.id)
}

We're still creating a Promise that will resolve to a post. However, we're not returning the Promise, so GraphQL is not aware of it and it will not wait for it to resolve. In JavaScript functions without an explicit return statement implicitly return undefined. So our function creates a Promise and then immediately returns undefined, causing GraphQL to return null for the field.

If the Promise returned by getPost rejects, we won't see any error listed in our response either -- because we didn't return the Promise, the underlying code doesn't care about whether it resolves or rejects. In fact, if the Promise rejects, you'll see an UnhandledPromiseRejectionWarning in your server console.

Fixing this issue is simple -- just add the return.

Common Scenario #5: Not chaining Promises correctly

You decide to log the result of your call to getPost, so you change your resolver to look something like this:

function post(root, args) {
  return getPost(args.id)
    .then(post => {
      console.log(post)
    })
}

When you run your query, you see the result logged in your console, but GraphQL resolves the field to null. Why?

When we call then on a Promise, we're effectively taking the value the Promise resolved to and returning a new Promise. You can think of it kind of like Array.map except for Promises. then can return a value, or another Promise. In either case, what's returned inside of then is "chained" onto the original Promise. Multiple Promises can be chained together like this by using multiple thens. Each Promise in the chain is resolved in sequence, and the final value is what's effectively resolved as the value of the original Promise.

In our example above, we returned nothing inside of the then, so the Promise resolved to undefined, which GraphQL converted to a null. To fix this, we have to return the posts:

function post(root, args) {
  return getPost(args.id)
    .then(post => {
      console.log(post)
      return post // <----
    })
}

If you have multiple Promises you need to resolve inside your resolver, you have to chain them correctly by using then and returning the correct value. For example, if we need to call two other asynchronous functions (getFoo and getBar) before we can call getPost, we can do:

function post(root, args) {
  return getFoo()
    .then(foo => {
      // Do something with foo
      return getBar() // return next Promise in the chain
    })
    .then(bar => {
      // Do something with bar
      return getPost(args.id) // return next Promise in the chain
    })

Pro tip: If you're struggling with correctly chaining Promises, you may find async/await syntax to be cleaner and easier to work with.

Common Scenario #6

Before Promises, the standard way to handle asynchronous code was to use callbacks, or functions that would be called once the asynchronous work was completed. We might, for example, call mongoose's findOne method like this:

function post(root, args) {
  return Post.findOne({ where: { id: args.id } }, function (err, post) {
    return post
  })

The problem here is two-fold. One, a value that's returned inside a callback isn't used for anything (i.e. it's not passed to the underlying code in any way). Two, when we use a callback, Post.findOne doesn't return a Promise; it just returns undefined. In this example, our callback will be called, and if we log the value of post we'll see whatever was returned from the database. However, because we didn't use a Promise, GraphQL doesn't wait for this callback to complete -- it takes the return value (undefined) and uses that.

Most more popular libraries, including mongoose support Promises out of the box. Those that don't frequently have complimentary "wrapper" libraries that add this functionality. When working with GraphQL resolvers, you should avoid using methods that utilize a callback, and instead use ones that return Promises.

Pro tip: Libraries that support both callbacks and Promises frequently overload their functions in such a way that if a callback is not provided, the function will return a Promise. Check the library's documentation for details.

If you absolutely have to use a callback, you can also wrap the callback in a Promise:

function post(root, args) {
  return new Promise((resolve, reject) => {
    Post.findOne({ where: { id: args.id } }, function (err, post) {
      if (err) {
        reject(err)
      } else {
        resolve(post)
      }
    })
  })

这篇关于为什么 GraphQL 查询返回 null?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆