GraphQL:field.resolve() 结果在指令内未定义

Posted

技术标签:

【中文标题】GraphQL:field.resolve() 结果在指令内未定义【英文标题】:GraphQL: field.resolve() result is undefined inside directive 【发布时间】:2020-06-02 17:46:39 【问题描述】:

我一直在关注我在 Google 上找到的关于如何制作 AuthDirective 的所有结果,但我无法让它发挥作用。

我会留下所有文件,看看我是否在实现时出错,但我猜不是因为即使在 field.resolve 中没有任何条件的情况下我也进行了测试。

我发现的一个特别的东西是 field.resolve,带有 4 个参数,其中第一个总是未定义的,不确定是否可以,但只是作为一个额外的提示。

代码如下:

这是我的 typedef 的一部分

directive @isAuthenticated on FIELD_DEFINITION

scalar Date

type User 
    id: ID,
    name: String
    email: String
    password: String
    createdAt: Date


type Token 
    token: String


# IF I REMOVE @isAuthenticated this works!
type Query 
    user(name: String): User! @isAuthenticated 
    users: [User!]! @isAuthenticated


type Mutation 
    createUser(name: String, email: String, password: String): Boolean!
    login(email: String!, password: String!): Token!

这是我的架构:

import path from 'path';
import  makeExecutableSchema  from 'graphql-tools';
import  fileLoader, mergeResolvers, mergeTypes  from 'merge-graphql-schemas';
import schemaDirectives from './Directives';

const typedefsArray = fileLoader(path.join(__dirname, './**/*.graphql'));
const resolversArray = fileLoader(path.join(__dirname, './**/*.resolvers.ts'));

const typeDefs = mergeTypes(typedefsArray);
const resolvers = mergeResolvers(resolversArray);
const schema = makeExecutableSchema( typeDefs, resolvers, schemaDirectives );

export default schema;

这是我的 AuthDirective

import  defaultFieldResolver  from 'graphql';
import  AuthenticationError, SchemaDirectiveVisitor  from 'apollo-server-express';

class AuthDirective extends SchemaDirectiveVisitor 
  visitFieldDefinition(field: any) 
    const  resolver = defaultFieldResolver  = field;

    field.resolve = async function (...args: any) 
      const [_, __, context] = args;
      const  req  = context;
      const accessToken = req.headers.authorization
        ? req.headers.authorization.replace('Bearer ', '')
        : null;

      if (accessToken) 
        // here I was doing jwt.verify(accessToken) but I removed it for now to simplify
        const result = await resolver.apply(this, args);
        return result; // <--- THIS IS ALWAYS UNDEFINED
       else 
        throw new AuthenticationError('Unauthorized field');
      
    ;
  


export default AuthDirective;

位于 ./Directives 下,并从索引中导出为:

import AuthDirective from './auth';

export default 
  isAuthenticated: AuthDirective

最后是我的用户解析器。就像我之前说的,如果我从用户 graphql 查询中删除指令,它应该会正常工作:

import jwt from 'jsonwebtoken';
import  AuthenticationError, UserInputError  from 'apollo-server-express';
import  IUser  from './IUser';
import UserModel from './user.schema';
import  Config  from '../../config';

const  APP_SECRET  = Config;

const createToken = async (user: IUser, secret: string, expiresIn: string) => 
  const  id, email, name  = user;
  return jwt.sign( id, email, name , secret,  expiresIn );
;

const userResolvers = 
  Query: 
    user: (_: any,  name :  name: string ) => UserModel.findOne( name ).exec(),
    users: () => UserModel.find().exec()
  ,
  Mutation: 
    createUser: async (_: any, args: IUser) => 
      await new UserModel(args).save();
      return true;
    ,
    login: async (_: any,  email, password : IUser) => 
      const user = await UserModel.findOne( email ).exec();

      if (!user) 
        throw new UserInputError('No user found with this login credentials.');
      

      const isValid = await user.validatePassword(password);

      if (!isValid) 
        throw new AuthenticationError('Invalid password.');
      

      return  token: createToken(user, APP_SECRET, '30m') ;
    
  
;

export default userResolvers;

和我的服务器:

import morgan from 'morgan';
import express from 'express';
import bodyParser from 'body-parser';
import compression from 'compression';
import depthLimit from 'graphql-depth-limit';
import  ApolloServer  from 'apollo-server-express';

import  Config, DBConfig  from './config';
import schema from './domain';
const  PORT  = Config;

const app = express();

const apolloServer = new ApolloServer(
  schema,
  validationRules: [depthLimit(7)],
  context: ( req, res : any) => ( req, res )
);

app.use(express.json());
app.use(compression());
app.use(morgan('combined'));
app.use(bodyParser.urlencoded( extended: true ));
apolloServer.applyMiddleware( app );

DBConfig.init();

app.listen(PORT, () => console.log(`Server running on port $PORT`));

【问题讨论】:

【参考方案1】:

field 上没有 resolver 属性,所以这个

const  resolver = defaultFieldResolver  = field;

将始终导致resolver 具有defaultFieldResolver 的值。如果您在具有自定义解析器的某个字段上使用该指令,则永远不会调用该自定义解析器 - 仅使用默认行为,它确实可以返回 undefined

您需要确保使用正确的属性:

const  resolve = defaultFieldResolver  = field;

或者你可以在解构field时重命名结果变量:

const  resolve: resolver = defaultFieldResolver  = field;

【讨论】:

我的天啊,我觉得自己很愚蠢,测试了除此之外的所有内容!感谢您的快速回复

以上是关于GraphQL:field.resolve() 结果在指令内未定义的主要内容,如果未能解决你的问题,请参考以下文章

当我的 GraphQL 架构在运行时由 postgraphile(在其自己的容器中)自动生成时,如何将中继编译器作为离线构建步骤运行?

如何在 Graphql.js 中使用 GraphQL 订阅

GraphQl 和 insomnia 桌面客户端无法使用 graphql.org/swapi-graphql

使用 Express-GraphQL 的 GraphQL 订阅

数组中类型为 graphql 对象的 GraphQL 字段

Graphql 突变查询不适用于 express-graphql