Skip to content

Commit 550da75

Browse files
authored
Fix: BadMapError when page param is not using bracket notation (#407)
When clients send page parameter as a string (e.g., page={"limit":1}) instead of bracket notation (page[limit]=1), the pagination code would crash with a BadMapError when calling Map.fetch on the string. This adds a guard clause to fetch_pagination_parameters that detects when page is a binary string and returns a structured 400 error explaining the correct format, instead of crashing with a 500. JSON:API spec requires bracket notation for nested query parameters.
1 parent 42a1fd5 commit 550da75

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

lib/ash_json_api/controllers/helpers.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,17 @@ defmodule AshJsonApi.Controllers.Helpers do
933933

934934
# This doesn't need to use chain, because its stateless and safe to
935935
# do anytime. Returning multiple errors is a nice feature of JSON API
936+
def fetch_pagination_parameters(%{query_params: %{"page" => page}} = request)
937+
when is_binary(page) do
938+
Request.add_error(
939+
request,
940+
Error.InvalidPagination.exception(
941+
detail: "page parameter must use bracket notation (e.g., page[limit]=10&page[offset]=0)"
942+
),
943+
:read
944+
)
945+
end
946+
936947
def fetch_pagination_parameters(request) do
937948
if request.action.type == :read do
938949
request

test/acceptance/index_pagination_test.exs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,20 @@ defmodule Test.Acceptance.IndexPaginationTest do
116116
data = response.resp_body["data"]
117117
assert length(data) == 5
118118
end
119+
120+
@tag capture_log: true
121+
test "returns 400 when page parameter is not using bracket notation" do
122+
# Clients must use page[limit]=10 format, not page={"limit":10} or similar
123+
# URL-encoded: %7B%22limit%22%3A1%7D = {"limit":1}
124+
response =
125+
Domain
126+
|> get("/posts?page=%7B%22limit%22%3A1%7D", status: 400)
127+
128+
errors = response.resp_body["errors"]
129+
130+
assert Enum.any?(errors, fn error ->
131+
error["code"] == "invalid_pagination" and error["detail"] =~ "bracket notation"
132+
end)
133+
end
119134
end
120135
end

0 commit comments

Comments
 (0)