AgentSkillsCN

graphile-v5-connection-filter

在PostGraphile v5中配置连接过滤器。当被要求“新增过滤条件”、“设置过滤器”、“按列筛选”、“禁用关系过滤器”、“启用所有列过滤器”,或在搭建连接过滤器插件时,此功能定能派上用场。

SKILL.md
--- frontmatter
name: graphile-v5-connection-filter
description: Configure connection filtering in PostGraphile v5. Use when asked to "add filtering", "configure filters", "filter by column", "disable relation filters", "enable all column filters", or when setting up the connection filter plugin.
compatibility: PostGraphile v5+, postgraphile-plugin-connection-filter v3+
metadata:
  author: constructive-io
  version: "1.0.0"

PostGraphile v5 Connection Filter

Configure powerful filtering capabilities for your GraphQL connections.

Official Documentation

When to Apply

Use this skill when:

  • Setting up filtering on connections
  • Configuring which columns can be filtered
  • Disabling relation filters to reduce API surface
  • Enabling filtering on non-indexed columns
  • Understanding filter operators

Quick Start

Installation

bash
pnpm add postgraphile-plugin-connection-filter@^3.0.0-rc.1

Basic Setup

typescript
import { PostGraphileConnectionFilterPreset } from 'postgraphile-plugin-connection-filter';

const preset: GraphileConfig.Preset = {
  extends: [PostGraphileConnectionFilterPreset],
};

Filter vs Condition

PostGraphile provides two ways to filter connections:

Featurecondition (built-in)filter (plugin)
OperatorsEquality onlyeq, ne, lt, gt, in, contains, etc.
LogicalNoand, or, not
ArraysNoYes
RelationsNoOptional

Configuration Options

Schema Options

typescript
const preset: GraphileConfig.Preset = {
  extends: [PostGraphileConnectionFilterPreset],
  schema: {
    // Disable relation filters (recommended for cleaner API)
    connectionFilterRelations: false,
    
    // Disable computed column filters
    connectionFilterComputedColumns: false,
    
    // Disable setof function filters
    connectionFilterSetofFunctions: false,
    
    // Keep logical operators (and, or, not)
    connectionFilterLogicalOperators: true,
    
    // Allow array filtering
    connectionFilterArrays: true,
  },
};

Disabling Relation Filters

The connectionFilterRelations: false option doesn't fully work. Use disablePlugins instead:

typescript
const preset: GraphileConfig.Preset = {
  extends: [PostGraphileConnectionFilterPreset],
  disablePlugins: [
    'PgConnectionArgFilterBackwardRelationsPlugin',
    'PgConnectionArgFilterForwardRelationsPlugin',
  ],
  schema: {
    // Set for documentation purposes
    connectionFilterRelations: false,
  },
};

Filter Operators

Comparison Operators

graphql
query {
  users(filter: {
    age: { eq: 25 }           # Equal
    age: { ne: 25 }           # Not equal
    age: { lt: 30 }           # Less than
    age: { lte: 30 }          # Less than or equal
    age: { gt: 18 }           # Greater than
    age: { gte: 18 }          # Greater than or equal
    age: { in: [25, 30, 35] } # In list
    age: { notIn: [0, -1] }   # Not in list
  }) {
    nodes { id name age }
  }
}

String Operators

graphql
query {
  users(filter: {
    name: { eq: "John" }
    name: { ne: "Jane" }
    name: { like: "J%" }              # SQL LIKE
    name: { ilike: "j%" }             # Case-insensitive LIKE
    name: { notLike: "Admin%" }
    name: { startsWith: "J" }
    name: { endsWith: "son" }
    name: { contains: "oh" }
    name: { notContains: "admin" }
    name: { in: ["John", "Jane"] }
  }) {
    nodes { id name }
  }
}

Null Checks

graphql
query {
  users(filter: {
    deletedAt: { isNull: true }   # IS NULL
    email: { isNull: false }      # IS NOT NULL
  }) {
    nodes { id name }
  }
}

Logical Operators

graphql
query {
  users(filter: {
    or: [
      { role: { eq: "ADMIN" } },
      { role: { eq: "MODERATOR" } }
    ]
  }) {
    nodes { id name role }
  }
}

query {
  users(filter: {
    and: [
      { age: { gte: 18 } },
      { age: { lt: 65 } }
    ]
  }) {
    nodes { id name age }
  }
}

query {
  users(filter: {
    not: { status: { eq: "BANNED" } }
  }) {
    nodes { id name status }
  }
}

Array Operators

graphql
query {
  posts(filter: {
    tags: { contains: ["important"] }      # Array contains value
    tags: { containedBy: ["a", "b", "c"] } # Array is subset
    tags: { overlaps: ["urgent", "hot"] }  # Arrays have common elements
    tags: { anyEqualTo: "featured" }       # Any element equals
  }) {
    nodes { id title tags }
  }
}

Enabling Filtering on All Columns

By default, PostGraphile only allows filtering on indexed columns. To enable filtering on all columns:

typescript
export const EnableAllFilterColumnsPlugin: GraphileConfig.Plugin = {
  name: 'EnableAllFilterColumnsPlugin',
  version: '1.0.0',

  schema: {
    entityBehavior: {
      pgCodecAttribute: {
        inferred: {
          after: ['postInferred'],
          provides: ['enableAllFilters'],
          callback(behavior) {
            return [behavior, 'filterBy'];
          },
        },
      },
    },
  },
};

Performance Warning: Filtering on non-indexed columns can cause slow queries. Monitor performance and add indexes as needed.

Disabling Filtering on Specific Columns

Use smart tags to disable filtering on sensitive columns:

sql
COMMENT ON COLUMN users.password_hash IS E'@behavior -filterBy';
COMMENT ON COLUMN users.internal_id IS E'@behavior -filterBy';

Or use a plugin:

typescript
export const DisableSensitiveFiltersPlugin: GraphileConfig.Plugin = {
  name: 'DisableSensitiveFiltersPlugin',
  version: '1.0.0',

  schema: {
    entityBehavior: {
      pgCodecAttribute: {
        override: {
          provides: ['disableSensitiveFilters'],
          callback(behavior, [codec, attributeName]) {
            const sensitivePatterns = ['password', 'secret', 'token', 'hash'];
            if (sensitivePatterns.some(p => attributeName.toLowerCase().includes(p))) {
              return [behavior, '-filterBy'];
            }
            return behavior;
          },
        },
      },
    },
  },
};

Complete Preset Example

typescript
import type { GraphileConfig } from 'graphile-config';
import { PostGraphileConnectionFilterPreset } from 'postgraphile-plugin-connection-filter';

const EnableAllFilterColumnsPlugin: GraphileConfig.Plugin = {
  name: 'EnableAllFilterColumnsPlugin',
  version: '1.0.0',
  schema: {
    entityBehavior: {
      pgCodecAttribute: {
        inferred: {
          after: ['postInferred'],
          provides: ['enableAllFilters'],
          callback(behavior) {
            return [behavior, 'filterBy'];
          },
        },
      },
    },
  },
};

export const FilterPreset: GraphileConfig.Preset = {
  extends: [PostGraphileConnectionFilterPreset],
  plugins: [EnableAllFilterColumnsPlugin],
  disablePlugins: [
    'PgConnectionArgFilterBackwardRelationsPlugin',
    'PgConnectionArgFilterForwardRelationsPlugin',
  ],
  schema: {
    connectionFilterRelations: false,
    connectionFilterComputedColumns: false,
    connectionFilterSetofFunctions: false,
    connectionFilterLogicalOperators: true,
    connectionFilterArrays: true,
  },
};

Troubleshooting

IssueSolution
Relation filters still appearUse disablePlugins instead of schema option
Column not filterableCheck if column is indexed or use EnableAllFilterColumnsPlugin
Filter type missingEnsure connection filter preset is in extends
Performance issuesAdd indexes for frequently filtered columns

Source Code References

References