# `Malla.Request`
[🔗](https://github.com/netkubes/malla/blob/main/lib/malla/request.ex#L21)

Client-side API for making structured requests to remote services.

This module provides the `request/4` function and `req/2` macro for invoking
operations on remote services using Malla's request protocol. The protocol
adds distributed tracing, standardized responses, retry logic, and plugin
interception on top of basic RPC.

Usage of this mechanism is optional. You can always use basic Malla
RPC using `Malla.remote/4` where this tools is based on.

This tool requires the usage of `Malla.Status` and `Malla.Tracer`.
When including plugin `Malla.Plugins.Request` in your service, it will
include them too.

## Quick Example

```elixir
defmodule APIService do
  use Malla.Service
  use Malla.Request  # Import req macro

  def get_user_info(user_id) do
    # Using macro syntax
    case req UserService.get_user(user_id) do
      {:ok, user} -> format_response(user)
      {:error, %Malla.Status{}} -> render_error()
    end
  end
end
```

## Core Functions

- `request/4` - Make a structured request to a remote service.
- `request!/4` - Same as `request/4` but raises on error.
- `req/1`, `req/2` - Macro versions for cleaner syntax.

## Request Flow

1. Creates "request-out" span for tracing
2. Calls `malla_request/3` callback (interceptable by plugins)
3. Uses `Malla.remote/4` for RPC to target service
4. Remote side creates "request-in" span
5. Executes target function
6. Normalizes response via `Malla.Status`
7. Emits telemetry events

## Response Types

Request handlers must return one of:

- `:ok` - Success, no data
- `:created` - Resource created, no data
- `{:ok, data}` - Success with data (map or list)
- `{:created, data}` - Resource created with data
- `{:error, term}` - Error (normalized via `Malla.Status`)
- `{:status, term}` - Custom status (normalized via `Malla.Status`)

## Options

Both `request/4` and `req/2` accept options:

- `:timeout` - Request timeout in milliseconds (default: 30_000)
- `:direct` - Skip RPC, call locally (default: false)
- `:retries` - Number of retries on failure
- `:retry_delay` - Delay between retries in milliseconds
- Plus any custom options for plugin use

## Examples

```elixir
# Using req macro (recommended)
use Malla.Request
{:ok, user} = req UserService.get_user(123)
{:created, post} = req BlogService.create_post(params)

# Using explicit function
Malla.Request.request(UserService, :get_user, [123])
Malla.Request.request(UserService, :update_user, [123, params], timeout: 10_000)

# With custom options
req PaymentService.charge(order_id),
  timeout: 15_000,
  auth_token: token

# Raising version
{:ok, user} = request!(UserService, :get_user, [123])
```

## Telemetry

Emits `[:malla, :request, :out]` events with:
- Measurements: `:counter`, `:duration`
- Metadata: `:target`, `:op`, `:result`, plus standard fields

## For Complete Documentation

See the [Request Handling](guides/08-distribution/04-request-handling.md) guide
for comprehensive documentation including:
- Plugin-based interception (auth, rate limiting, validation)
- Distributed tracing patterns
- Error handling strategies
- Testing approaches
- Best practices

## See Also

- `Malla.Plugins.Request` - Server-side request handling and plugin interface
- `Malla.Node` - Lower-level RPC functions
- `Malla.Status` - Response normalization

# `op`

```elixir
@type op() :: [:atom] | String.t()
```

# `req_opt`

```elixir
@type req_opt() :: Malla.remote_opt() | {:direct, boolean()}
```

# `req`
*macro* 

  Macro to perform a call to a callback defined in a local or remote service

  Must be used as 'remote Service.fun(args), calls Malla.Mode.cb(Service, fun, args)

# `req`
*macro* 

# `request`

```elixir
@spec request(Malla.id(), atom(), [any()], [req_opt()]) ::
  {:ok, map()} | {:created, map()} | {:error, map()} | {:status, map()}
```

Calls remote request inside a span flow

* Starts a span "request" in defined service and uses cb/3 to call
  callback `malla_spans_request` in remote service
* Trace information will be sent to continue the trace flow
* If remote service is not active, see cb/3 for "service_not_available" replies
* Call will be sent to remote's request/3 callback
* If `direct` is used, the request is called locally, skipping the call to `scallb/4`
* Remote request is expected to return specific responses

# `request!`

```elixir
@spec request!(Malla.id(), atom(), [any()], [req_opt()]) ::
  {:created, map()} | {:error, map()} | {:ok, map()} | {:status, map()}
```

Works like `req/2` but it will raise exception `ReqError` if an error is produced.
`type` in exception can be either :service_not_available or :internal_error

---

*Consult [api-reference.md](api-reference.md) for complete listing*
