2
// didn't add node definitions because this problem occurs on initial data fetch

// user type
const userType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
      id: globalIdField('User'),
      email: { type: GraphQLString },
      posts: {
        type: postConnection,
        args: connectionArgs,
        // getUserPosts() function is below
        resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args),
      },
  }),
  interfaces: [nodeInterface],
})

// post type
const postType = new GraphQLObjectType({
  name: 'Post',
  fields: () => ({
    id: globalIdField('Post'),
    title: { type: GraphQLString },
    content: { type: GraphQLString },
  }),
  interfaces: [nodeInterface],
})  

// connection type
const {connectionType: postConnection} =
  connectionDefinitions({name: 'Post', nodeType: postType})

// Mongoose query on other file
exports.getUserPosts = (userid) => {
  return new Promise((resolve, reject) => {
    Post.find({'author': userid}).exec((err, res) => {
      err ? reject(err) : resolve(res)
    })
  })
}

ブラウザ コンソールに次の警告が表示されます。

クエリのサーバー要求はApp、次の理由で失敗しました:

  1. 未定義のプロパティ「長さ」を読み取ることができません

    _posts40BFVD:posts(first:10) {

    ^^^


それが私が得た唯一の情報であり、これ以上エラーや参照はありません。その理由は何ですか?

このコードはRelay-starter-kitWidgetからのものです。すべてのコードをに置き換えただけPostです。すべてがスターターとほぼ同じであるため、原因はデータベース コードのどこかにあると思います。

しかし、同じ構造を返すため、問題がわかりませんgetUserPosts():オブジェクトの配列..


なにが問題だったの?

resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args)

getUserPosts()promise を返しました (コードをブロックするのはおそらく悪い考えです) が、コールバックはありませんでした。私の意見ではconnectionFromArray()、コードの実行は続行されましたが、getUserPosts()まだデータがなかったため、システム全体が失敗しました。

考えられる解決策の 1 つ

とにかくBabelとJavaScriptの「将来の機能」を使用するので、asyncandを使用することにしawaitました。しかし、まだ問題がありgetUserPosts()、空の配列が返されました。

await次に、async関数内で別の関数が呼び出された場合、別の関数も同様に呼び出される必要があることを発見しました。asyncそうしないと、すべてawait-s失敗します。動作する私の最終的な解決策は次のとおりです。

// async function that makes db query
exports.getUserPosts = async (userId) => {
    try {
      // db query
      const posts = await Post.find({author: args}).exec()
      return posts
    } catch (err) {
      return err
    }
}

// and resolve method in Schema, also async
resolve: async (user, args) => {
  const posts = await getUserPosts(user._id)
  return connectionFromArray(posts, args)
}

それが最善の方法であるかどうかはまだわかりません。私の論理では、ノードでは可能な限り async を使用する必要がありますが、私はノードの専門家からはほど遠いと言っています。詳細がわかり次第、質問を更新します。

Relayを使用してこのデータベースクエリの状況に対処するためのより良い方法、または推奨される方法があるかどうかを知りたい.

4

2 に答える 2

4

問題はフィールドのresolve機能にありpostsます。

resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args),

まず、getUserPostspromise を返す非同期関数です。関数を書くときは、それを考慮に入れる必要がありresolveます。次に、モジュールのヘルパー関数user.idによって生成されるグローバル ID フィールドです。その ID をローカル ID に変換する必要があります。graphql-relayglobalIdField

リレー スターター キットを使用すると、 とを使用できます 。コードは次のようになります。asyncawait

resolve: async (user, args) => {
  let userId = fromGlobalId(user.id).id;
  // convert this userId string to Mongo ID if needed
  // userId = mongoose.Types.ObjectId(userId).
  const posts = await getUserPosts(userId);
  return connectionFromArray(posts, args),
},

お使いのasyncバージョンのgetUserPostsでは、errエラーが発生した場合にオブジェクトが返されます。

exports.getUserPosts = async (userId) => {
  try {
    // db query
    const posts = await Post.find({author: args}).exec()
    return posts
  } catch (err) {
    return err
  }
}

エラーを再スローするか、空の配列を返すことをお勧めします。

于 2016-05-31T19:55:45.060 に答える
3

graphql-relay は、プロミスが解決されるまで待機するconnectionFromPromisedArray関数を提供します。resolve: (user, args) => connectionFromPromisedArray(getUserPosts(user.id), args),これは、約束された接続を処理するときにおそらく最も推奨される/最も簡単な方法です。

グローバル IDuser.idフィールドの場合、base64 文字列から実際の DB ID を抽出する必要があります。 またconst { type, id } = fromGlobalId(user.id);

resolve: (user, args) => connectionFromPromisedArray(getUserPosts(fromGlobalId(user.id).id), args),

RelayとMongooseを使用している私が取り組んでいるアプリでは、アプリレベルではなくDBレベルでページネーションとフィルタリングを処理する独自の接続実装を作成する方が良いことがわかりました。これを書いたとき、落書きマングースから多くのインスピレーションを得ました。

于 2016-06-02T12:38:32.903 に答える