【问题标题】:how to implement resolveType function / initialise graphQL server in express如何在express中实现resolveType函数/初始化graphQL服务器
【发布时间】:2020-11-07 09:15:08
【问题描述】:

我有 2 个关于 GraphQL 的问题。

  1. 我使用makeExecutableSchema 创建了一个模式,所以我可以创建一个executableSchema。 我像这样导出executableSchema 并在我的app.js 中初始化服务器。
app.use('/graphql', graphqlHTTP({
    schema: executableSchema,
    rootValue: executableSchema,
    graphiql: true,
    customFormatErrorFn: (error) => ({
        message: error.message,
        locations: error.locations,
        stack: error.stack ? error.stack.split('\n') : [],
        path: error.path
    })
}))

但我想知道为schemarootValue 传递executableSchema 是否是正确的方法?

  1. 如何实现resolveType 函数以及在哪里实现?

我收到一条错误消息 Error: Abstract type "LoginResult" must resolve to an Object type at runtime for field "Query.login" with value { __typename: "EmailNotFound" }, received "undefined". Either the "LoginResult" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.

一般来说,我将LoginResult 创建为union,这样我就可以在前端收到更具体的错误消息,以处理不同的错误消息。

这就是我在schema 中的LoginResult 的样子

    type AuthData {
        token: String!
        refreshToken: String!
        userId: String!
    }

    type EmailError {
        message: String
    }

    type PasswordError {
        message: String
    }

    type VerificationError {
        message: String
    }

    union LoginResult = AuthData | EmailError | PasswordError | VerificationError

    type Query {
        login(email: String!, password: String!): LoginResult!
    }

login解析器方法看起来像这样

const resolvers = {
    Query: {
        login: async function ({ email, password }) {
            try {
                const user = await User.findOne({ email: email })
    
                const errors = []
    
                if(!user) {
                    const error = new Error('User not found.')
                    error.code = 404
                    errors.push('404')
                    
                    return {
                        __typename: 'EmailNotFound'
                    }
    
                    // throw error
                }
    
                const isEqual = await bcrypt.compare(password, user.password)
    
                if(!isEqual) {
                    const error = new Error('Password is incorrect.')
                    error.code = 401
                    errors.push('401')
                    // throw error
                    return {
                        __typename: 'PasswordIncorrect'
                    }
                }
    
                if(!user.isVerified) {
                    const error = new Error('Please verify your email address')
                    error.code = 403
                    errors.push('403')
                    // throw error
                    return {
                        __typename: 'NotVerified'
                    }
                }
    
                // if (errors.length > 0) {
                //     const error = new Error('Invalid input.')
                //     error.data = errors
                //     error.code = 422
                //     throw error
                // }
    
                const token = jwt.sign(
                    {
                        userId: user._id.toString(),
                        email: user.email
                    },
                    JWT_SECRET_KEY,
                    { expiresIn: '30min' }
                )
    
                const refreshToken = jwt.sign(
                    {
                        userId: user._id.toString(),
                        email: user.email
                    },
                    JWT_SECRET_KEY,
                    { expiresIn: '1h' }
                )
    
                return {
                    token,
                    refreshToken,
                    userId: user._id.toString(),
                    __typename: 'AuthData'
                }
    
            } catch(err) {
                console.log(err)
            }
        }
    }
}

这就是我从前端创建查询的方式

const graphqlQuery = {
          query: `
            query UserLogin($email: String!, $password: String!){
              login(email: $email, password: $password) {
                __typename
                ...on AuthData {
                    token
                    userId
                }
                ...on EmailError {
                   message
                }
                ...on PasswordError {
                   message
                }
                ...on VerificationError {
                   message
                }

              }
            }
          `,
          variables: {
            email,
            password
          }
        }

如有任何帮助,我们将不胜感激!

【问题讨论】:

  • 您是如何创建架构的?使用buildSchema?
  • @DanielRearden 我正在使用'const makeExecutableSchema = require('@graphql-tools/schema').makeExecutableSchema' 它基于我阅读的这个stackoverflow,我发现'makeExecutableSchema'->@987654321 @

标签: node.js express graphql graphql-tools


【解决方案1】:

rootValue 是传递给您的查询和变异解析器的值(作为父值)。此值很少使用,但您可以使用它将任何内容传递给这些解析器。

您的类型名称和您在__typename 中返回的字符串必须完全相同。例如。您的类型似乎被称为PasswordError,但您返回类型名称PasswordIncorrect

【讨论】:

  • 我完全按照您的建议更改了它们,现在它显示不同的错误,显示“无法解构'未定义'的属性'电子邮件',因为它是未定义的”。想知道是什么导致了这个未定义?
  • 是的,解析器的第一个参数是父值而不是参数。更改解析器的签名:login: async function (parent, { email, password }, context) {
  • 非常感谢,它有效!另一个问题。 “父”值是否也是所有其他解析器方法中的第一个参数?它是“graphql-tools”语法吗?
  • 通常是第一个参数。这是由reference implementation 引入的,并已被许多框架和语言采用。如果您能接受并支持我的回答,那将对我有很大帮助:)
  • 是的,它始终是第一个值。 __typename 只需要由解析为联合类型或接口类型的解析器返回。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
  • 2011-07-20
  • 2012-04-24
  • 2020-07-31
  • 2023-03-18
  • 2018-01-18
相关资源
最近更新 更多