如何在express中实现resolveType函数/初始化graphQL服务器

Posted

技术标签:

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

我有 2 个关于 GraphQL 的问题。

    我使用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 是否是正确的方法?

    如何实现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' 它基于我阅读的这个***,我发现'makeExecutableSchema'->@987654321 @ 【参考方案1】:

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

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

【讨论】:

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

以上是关于如何在express中实现resolveType函数/初始化graphQL服务器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 nodejs 和 express 在 REST API 中实现搜索和过滤

如何使用express-validator在单独的文件中实现验证

如何在 node express 中实现 JWT(JsonWebToken) 注销

如何在 React/Express 应用程序中实现可选身份验证?

我想在 Rails 中实现 PayPal Digital Goods Express Checkout API

高阶函数式编程二:在 Kotlin 中实现单位半群(Monoid)