Skip to content

Filtering Collections and Globals - #38

Closed
fabioquarantini wants to merge 4 commits into
janbuchar:masterfrom
fabioquarantini:feature/collection-filtering
Closed

Filtering Collections and Globals#38
fabioquarantini wants to merge 4 commits into
janbuchar:masterfrom
fabioquarantini:feature/collection-filtering

Conversation

@fabioquarantini

@fabioquarantini fabioquarantini commented May 28, 2025

Copy link
Copy Markdown
Contributor

Filtering and Control Options

Collection and Global Filtering

Control which collections and globals are included in the OpenAPI specification:

import { openapi } from 'payload-oapi'

// Basic filtering with arrays
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Public API', version: '1.0.0' },
      
      // Include only specific collections
      includeCollections: ['posts', 'categories'],
      
      // Exclude sensitive globals
      excludeGlobals: ['internal-settings'],
      
      // Hide internal Payload collections (payload-preferences, payload-migrations)
      hideInternalCollections: true,
    }),
  ],
})

// Advanced filtering with functions
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Public API', version: '1.0.0' },
      
      // Custom filtering with functions
      includeCollections: ({ slug, config }) => {
        return config.auth || config.access?.read === true
      },
      
      excludeGlobals: ({ slug, config }) => {
        return slug.startsWith('internal-')
      },
    }),
  ],
})

Operation-Level Filtering

Control which CRUD operations are included for collections:

import { openapi } from 'payload-oapi'

// Include only specific operations
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Read-Only API', version: '1.0.0' },
      
      operations: {
        includeOperations: ['read', 'list'],
      },
    }),
  ],
})

// Exclude dangerous operations
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Safe API', version: '1.0.0' },
      
      operations: {
        excludeOperations: ['delete'],
      },
    }),
  ],
})

// Custom operation filtering per collection
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Custom API', version: '1.0.0' },
      
      operations: {
        operationFilter: (operation, collection) => {
          // Only allow read operations for sensitive collections
          if (collection.slug === 'users') {
            return ['read', 'list'].includes(operation)
          }
          // Allow all operations for other collections
          return true
        }
      },
    }),
  ],
})

Field-Level Filtering

Control which fields are exposed in the documentation:

import { openapi } from 'payload-oapi'

// Exclude specific fields by name
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Clean API', version: '1.0.0' },
      
      excludeFields: ['internalNotes', 'adminComments'],
    }),
  ],
})

// Custom field filtering by type and name
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Secure API', version: '1.0.0' },
      
      excludeFields: ({ name, type }) => {
        // Exclude all password-type fields and internal fields
        return type === 'password' || name.startsWith('_')
      },
    }),
  ],
})

// Advanced field filtering with collection context
buildConfig({
  plugins: [
    openapi({
      openapiVersion: '3.0',
      metadata: { title: 'Context-Aware API', version: '1.0.0' },
      
      excludeFields: ({ name, type, collection }) => {
        // Different filtering rules for different collections
        if (collection.slug === 'users') {
          // For users, exclude sensitive fields
          return ['password', 'salt', 'hash', 'resetPasswordToken'].includes(name)
        }
        
        if (collection.slug === 'posts') {
          // For posts, exclude internal fields and drafts
          return name.startsWith('_') || name === 'draft'
        }
        
        // For other collections, only exclude password fields
        return type === 'password'
      },
    }),
  ],
})

@janbuchar

Copy link
Copy Markdown
Owner

Hi @fabioquarantini and thanks for the contribution! Could you rebase this onto the current master branch to avoid conflicts?

@fabioquarantini
fabioquarantini force-pushed the feature/collection-filtering branch from 32ffe7f to a6ffbc5 Compare May 28, 2025 14:41
@fabioquarantini

Copy link
Copy Markdown
Contributor Author

I've rebased the branch

@fabioquarantini

Copy link
Copy Markdown
Contributor Author

Is the pull request okay for you?

@janbuchar

Copy link
Copy Markdown
Owner

Hi @fabioquarantini, I still haven't found the time to look into it - sorry for that. The huge change in the test snapshot kinda threw me off balance 🙂 Perhaps if you could sum up the changes in that and why it needed to happen, it would speed up the review process for me.

@fabioquarantini

Copy link
Copy Markdown
Contributor Author

Explaining it here isn’t easy. If you need any clarification, feel free to ask.

@janbuchar janbuchar left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not gonna lie, it is really hard to read this as a whole. I highlighted some places where refactoring may help, let me know what you think.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some unexpected changes in schema names etc. which make this hard to review. Any chance you could undo these?

Comment thread test/filtering.test.ts
const mockCollections = {
posts: {
config: { slug: 'posts', auth: false },
} as unknown as Collection,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write the config in a way that actually satisfies the Collection type instead of casting to unknown

Comment thread src/utils/filtering.ts

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of repetition that makes it hard to review this. Do you see any way to refactor this?

@janbuchar

Copy link
Copy Markdown
Owner

Thank you for the contribution @fabioquarantini. I will close this in favor of #75, which, while being less sophisticated, achieves a very similar result and is easier to review.

Although we might want to port over the callback-based filtering over later on, that would be a nice addition.

@janbuchar janbuchar closed this Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants