I’m running into a FunctionClauseError in my Absinthe resolver when an optional argument (cursor) is omitted from the query. Here’s my setup:
I’m defining a paginated query for recent reviews, using cursor-based pagination:
@desc "Get recent reviews (cursor-based pagination)"
field :recent_reviews, :review_connection do
arg :cursor, :string, default_value: nil
arg :limit, :integer, default_value: 10
resolve &Resolvers.list_recent_reviews/3
end
In my resolver, I expect both cursor and limit:
def list_recent_reviews(_parent, %{cursor: cursor, limit: limit}, _resolution) do
Reviews.list_recent_reviews(cursor, limit)
end
However, when I run this GraphQL query without providing cursor:
query RecentReviews {
recentReviews {
nextCursor
items {
id
}
}
}
I get this error:
[error] ** (FunctionClauseError) no function clause matching in KaguyaWeb.GraphQL.Reviews.Resolvers.list_recent_reviews/3
(kaguya 0.1.0) lib/kaguya_web/graphql/reviews/resolvers.ex:39:
KaguyaWeb.GraphQL.Reviews.Resolvers.list_recent_reviews(%{}, %{limit: 10}, #Absinthe.Resolution{})
What I Found So Far
- Even though I set default_value: nil in the schema, Absinthe omits cursor entirely from args when it’s not provided.
- My function clause expects %{cursor: cursor, limit: limit}, but if cursor is missing, it doesn’t match, causing the FunctionClauseError.
Temporary Fix
I changed the resolver to use Map.get/3 to avoid a missing key issue:
def list_recent_reviews(_parent, args, _resolution) do
cursor = Map.get(args, :cursor, nil)
limit = Map.get(args, :limit, 10)
Reviews.list_recent_reviews(cursor, limit)
end
This works fine, but I’m wondering:
- Is this the best way to handle optional GraphQL arguments in Absinthe?
- Shouldn’t default_value: nil ensure that cursor is always present in args, even if the user doesn’t provide it?
- Why does Absinthe sometimes omit cursor entirely, rather than including it as nil?