caramel
Skip to content
Browse documentation
Cookbook / Requests and responses

Return JSON from an action.

Serve the same resource to a browser and a client that asks for JSON. Start with a generated Book resource.

Draft recipeTarget: 0.4.0Not yet recipe-tested

Where the change goes

app/actions/books/index.crReturn the data
spec/requests/books_spec.crCheck the response

Keep the result explicit

The generated action returns records from handle. Its render method supplies the HTML view. Clients that send Accept: application/json receive JSON.

app/actions/books/index.cr · handle excerpt
def handle(contract : Contract)
  {records: App::Book.query
    .order_by(:id, :desc)
    .limit(100).to_a}
end

Prove the response

spec/requests/books_spec.cr · inside a spec
Corretto.session do |client, db|
  response = client.get("/books",
    headers: {"Accept" => "application/json"})
  response.should have_status(200)
  JSON.parse(response.body)["records"]
    .as_a.should be_empty
end

This example assumes an empty spec database. Add a persisted-book case to verify the returned fields.

Terminal
frappe check
frappe corretto spec/requests/books_spec.cr
Returning JSON and accepting JSON are separate

The current typed request pipeline accepts URL-encoded and multipart forms. JSON request bodies are not supported there; writes also require CSRF validation. See the webhook gap before designing an external API.