caramel
Skip to content
Browse documentation
Cookbook / Requests and responses

Read and write JSON.

Use a generated Book resource for browser HTML, JSON responses, and typed JSON input through the same action contracts.

Draft recipeTarget: 0.6.1Not yet recipe-tested

Ask for a JSON response

A generated action returns records from handle and supplies HTML through render. A client sending Accept: application/json receives the action result as JSON.

spec/requests/books_spec.cr · inside an example
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.

Bind a JSON object to the contract

The default request policy reads Content-Type: application/json. Use a top-level object and the field’s JSON type: strings for String and Time, numbers for Int32, Int64 and Float64, and booleans for Bool. Null is absent; nested arrays and objects do not bind to fields. Duplicate members are errors.

spec/requests/books_spec.cr · inside an example
Corretto.session do |client, db|
  book = {title: "Dune", author: "Frank Herbert"}
  accept = {"Accept" => "application/json"}
  created = client.post("/books", json: book, headers: accept)
  created.should have_status(201)
  JSON.parse(created.body)["record"]["title"].as_s.should eq("Dune")
end

Keep browser writes protected

Same-origin browser fetch requests send the page’s CSRF token as X-CSRF-Token. A JSON member named _csrf does not supply that token, and _method does not override the HTTP method. Corretto adds the origin, cookie, and CSRF header automatically; override a header to test rejection.

Corretto · inside a session
forged = {"Accept" => "application/json", "X-CSRF-Token" => "forged"}
client.post("/books", json: book, headers: forged).should have_status(403)

Check the failure cases

Malformed JSON returns 400. A valid JSON value that is not an object returns 422. Wrong field types and contract or changeset errors return 422; JSON clients receive an errors object. Unsupported media types, including application/*+json, return 415 under the default policy. An action can use raw ingress when it needs a different JSON format.

Terminal
frappe check
frappe corretto spec/requests/books_spec.cr
External callers need their own credential

An API that skips browser CSRF must declare an authenticator with ingress. Its session is empty and unsaved; authenticate with a bearer token or signature that a browser does not attach automatically. The webhook guide shows the request-policy boundary.