You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo GraphQL-Tools:如何修改默认解析器行为

How to Customize GraphQL's Default Resolver Behavior

Great question! You're correct that GraphQL.js falls back to a default resolver for any field without an explicit one — this resolver either returns the matching property on the source object or calls a method of the same name. If you want to replace this default logic with your own custom behavior, there are a few practical approaches depending on how broad you need the change to be:


Approach 1: Override the Global defaultFieldResolver

GraphQL.js exposes the defaultFieldResolver function directly, so you can replace it to change behavior for all fields across all your schemas. This is the most sweeping approach:

const { defaultFieldResolver } = require('graphql');

// Define your custom default resolver logic
const myCustomDefaultResolver = (source, args, context, info) => {
  console.log(`Resolving field: ${info.fieldName}`);
  
  // Example: Fallback to a default value if the property doesn't exist
  const value = source[info.fieldName];
  if (typeof value === 'function') {
    return value(args, context, info);
  }
  return value ?? 'N/A';
};

// Replace the global default resolver
require('graphql').defaultFieldResolver = myCustomDefaultResolver;

Note: This will affect every GraphQL schema in your application, so use it carefully if you're maintaining multiple schemas with different requirements.


Approach 2: Set Type-Level Default Resolvers with graphql-tools

If you only want to customize behavior for a specific schema or type, you can use the special __resolveField property in your resolver map. This tells graphql-tools to use your custom logic for any field on that type that doesn't have its own explicit resolver:

const { makeExecutableSchema } = require('@graphql-tools/schema');

const typeDefs = `
  type User {
    id: ID!
    name: String
    email: String
  }

  type Query {
    user: User
  }
`;

const resolvers = {
  User: {
    __resolveField: (source, args, context, info) => {
      // Custom logic: Mask sensitive fields like email
      if (info.fieldName === 'email') {
        const [localPart, domain] = source.email.split('@');
        return `${localPart.substring(0, 2)}****@${domain}`;
      }
      // Fallback to standard behavior for other fields, or add your own logic
      return source[info.fieldName] ?? 'Not provided';
    }
  },
  Query: {
    user: () => ({ id: '1', name: 'Alice', email: 'alice@example.com' })
  }
};

const schema = makeExecutableSchema({ typeDefs, resolvers });

Here, any field on the User type without an explicit resolver will use the __resolveField logic, while other types remain unaffected.


Approach 3: Use Schema Directives for Granular Control

For the most precision — only applying custom logic to specific fields or types — you can create a custom schema directive. This lets you tag fields that should use your default resolver behavior:

const { makeExecutableSchema } = require('@graphql-tools/schema');
const { SchemaDirectiveVisitor } = require('@graphql-tools/utils');
const { defaultFieldResolver } = require('graphql');

// Define your custom directive logic
class CustomDefaultDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field) {
    // Preserve the original resolver if one exists
    const originalResolver = field.resolve || defaultFieldResolver;
    
    // Wrap the resolver with your custom logic
    field.resolve = async (source, args, context, info) => {
      console.log(`Processing field: ${info.fieldName}`);
      
      // Run the original resolver logic
      const result = await originalResolver(source, args, context, info);
      
      // Modify the result as needed (e.g., replace nulls)
      return result ?? 'Default Value';
    };
  }
}

const typeDefs = `
  directive @customDefault on FIELD_DEFINITION

  type User {
    id: ID!
    name: String @customDefault
    email: String @customDefault
  }

  type Query {
    user: User
  }
`;

const resolvers = {
  Query: {
    user: () => ({ id: '1', name: null, email: 'bob@example.com' })
  }
};

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
  schemaDirectives: {
    customDefault: CustomDefaultDirective
  }
});

This approach is perfect if you don't want to change behavior globally or per-type, but only for specific fields you explicitly tag.


Quick Recap:

  • Global change: Override the built-in defaultFieldResolver
  • Per-type change: Use the __resolveField property on your type resolver
  • Per-field control: Create a custom schema directive

内容的提问来源于stack exchange,提问作者Sacha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:56:39