# Customizing Requests with `prepare_req`

Every client module generated by `use Grephql` includes an overridable `prepare_req/1` callback. It receives the fully built `%Req.Request{}` just before it is sent, giving you a hook to attach [Req response steps](https://hexdocs.pm/req/Req.Request.html#module-response-steps), add headers, or apply any other request-level customization.

## How It Works

1. Override `prepare_req/1` in your client module
2. In a Req response step, use `Grephql.Result.put_resp_assign/3` to stash values from the response
3. After decoding, those values appear in `result.assigns`

```elixir
defmodule MyApp.API do
  use Grephql,
    otp_app: :my_app,
    source: "priv/schemas/api.json",
    endpoint: "https://api.example.com/graphql"

  def prepare_req(req) do
    Req.Request.append_response_steps(req,
      capture_request_id: fn {req, resp} ->
        request_id = Req.Response.get_header(resp, "x-request-id")
        {req, Grephql.Result.put_resp_assign(resp, :request_id, request_id)}
      end
    )
  end

  defgql :get_user, ~GQL"""
    query GetUser($id: ID!) {
      user(id: $id) { name }
    }
  """
end

{:ok, result} = MyApp.API.get_user(%{id: "1"})
result.assigns[:request_id]  #=> ["req-abc-123"]
```

## Storing Multiple Values

Call `put_resp_assign/3` multiple times to store different pieces of metadata:

```elixir
def prepare_req(req) do
  Req.Request.append_response_steps(req,
    capture_metadata: fn {req, resp} ->
      resp =
        resp
        |> Grephql.Result.put_resp_assign(:extensions, resp.body["extensions"])
        |> Grephql.Result.put_resp_assign(:request_id, Req.Response.get_header(resp, "x-request-id"))

      {req, resp}
    end
  )
end
```

## Testing

Use `Req.Test` as usual. Include the metadata you want to capture in the stubbed response:

```elixir
test "captures request metadata" do
  Req.Test.stub(MyApp.API, fn conn ->
    conn
    |> Plug.Conn.put_resp_header("x-request-id", "test-123")
    |> Req.Test.json(%{
      "data" => %{"user" => %{"name" => "Alice"}}
    })
  end)

  assert {:ok, result} = MyApp.API.get_user(%{id: "1"})
  assert result.assigns[:request_id] == ["test-123"]
end
```

## Example: Shopify Rate Limiting via Extensions

Shopify's GraphQL API returns rate-limit and cost information in the `extensions` field:

```json
{
  "data": { "products": { ... } },
  "extensions": {
    "cost": {
      "requestedQueryCost": 12,
      "actualQueryCost": 10,
      "throttleStatus": {
        "currentlyAvailable": 980,
        "maximumAvailable": 1000.0,
        "restoreRate": 50.0
      }
    }
  }
}
```

Capture it with `prepare_req/1`:

```elixir
defmodule MyApp.Shopify do
  use Grephql,
    otp_app: :my_app,
    source: "priv/schemas/shopify.json",
    endpoint: "https://myshop.myshopify.com/admin/api/graphql.json"

  def prepare_req(req) do
    Req.Request.append_response_steps(req,
      capture_extensions: fn {req, resp} ->
        {req, Grephql.Result.put_resp_assign(resp, :extensions, resp.body["extensions"])}
      end
    )
  end

  defgql :get_products, ~GQL"""
    query GetProducts($first: Int!) {
      products(first: $first) {
        edges {
          node {
            title
          }
        }
      }
    }
  """
end
```

Then use the captured cost data to throttle API calls:

```elixir
{:ok, result} = MyApp.Shopify.get_products(%{first: 10})

throttle_status = result.assigns[:extensions]["cost"]["throttleStatus"]
throttle_status["currentlyAvailable"]  #=> 980
throttle_status["restoreRate"]         #=> 50.0
```

```elixir
defmodule MyApp.Shopify.RateLimiter do
  def maybe_throttle(%Grephql.Result{assigns: assigns}) do
    case assigns[:extensions] do
      %{"cost" => %{"throttleStatus" => %{"currentlyAvailable" => available}}}
      when available < 100 ->
        Process.sleep(1_000)

      _other ->
        :ok
    end
  end
end
```

## API Reference

- `prepare_req/1` — overridable callback on client modules, receives the `%Req.Request{}` before it is sent
- `Grephql.Result.put_resp_assign/3` — stores a key-value pair on the Req response for later transfer to `Result.assigns`
- `Grephql.Result` — the `:assigns` field (default `%{}`) holds all values stored via `put_resp_assign/3`
