graphqlstrawberry-graphql

Strawberry GraphQL filter on nested field


I am building an API using Strawberry GraphQL and FastAPI. I was thinking about providing query with filters attached to the nested field, for example:

query {
  users {
    pets(type: DOG) {
      name
    }
}

I couldn't find a way to implement it though. I saw examples were similar problems were solved with filter on the top level like:

query {
  users(where: {pets: {type: DOG}) {
    pets(type: DOG) {
      name
    }
}

Is the first query an allowed pattern in GraphQL at all? If so, how can it be implemented in Strawberry?


Solution

  • In GraphQL, the first query pattern you mentioned—filtering nested fields directly—is not typically supported out of the box. GraphQL queries are usually designed to specify filters at the top level. However, you can implement similar functionality in your API by designing your schema carefully and using resolvers.

    Here's how you can implement a solution for your FastAPI and Strawberry GraphQL setup:

    Step 1: Define Your Models

    Assuming you have User and Pet models, you might start with something like this:

    from typing import List
    import strawberry
    
    @strawberry.type
    class Pet:
        name: str
        type: str  # You can use an enum for types
    
    @strawberry.type
    class User:
        id: int
        name: str
        pets: List[Pet]
    

    Step 2: Define Your Query Type

    Next, create a query type that includes a resolver for users and their pets:

    @strawberry.type
    class Query:
        @strawberry.field
        def users(self, type: str = None) -> List[User]:
            # Replace this with your actual data fetching logic
            users_data = [
                User(id=1, name="Alice", pets=[Pet(name="Buddy", type="DOG"), Pet(name="Whiskers", type="CAT")]),
                User(id=2, name="Bob", pets=[Pet(name="Max", type="DOG")])
            ]
    
            if type:
                for user in users_data:
                    user.pets = [pet for pet in user.pets if pet.type == type]
    
            return users_data
    

    Step 3: Update the Schema

    Finally, you can create your schema and integrate it with FastAPI:

    import strawberry
    from fastapi import FastAPI
    from strawberry.fastapi import GraphQLRouter
    
    schema = strawberry.federation.Schema(query=Query)
    graphql_app = GraphQLRouter(schema)
    
    app = FastAPI()
    app.include_router(graphql_app, prefix="/graphql")
    

    Query Example

    With this setup, you can now query for users and filter their pets by type at the top level like this:

    query {
      users(type: "DOG") {
        pets {
          name
        }
      }
    }
    

    Filtering at the Top Level: Since GraphQL doesn’t support filtering nested fields directly in the query, we provided a type parameter in the users resolver.

    Dynamic Resolvers: The resolver filters pets based on the provided type before returning the users.

    This method adheres to GraphQL best practices while still allowing you to achieve the desired filtering effect. If you want more complex nested filtering, you might need to expand your query parameters and logic accordingly.