Malla.Request (malla v0.0.1-rc.1)

Copy Markdown View Source

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

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 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

# 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 guide for comprehensive documentation including:

  • Plugin-based interception (auth, rate limiting, validation)
  • Distributed tracing patterns
  • Error handling strategies
  • Testing approaches
  • Best practices

See Also

Summary

Functions

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

Calls remote request inside a span flow

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

Types

op()

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

req_opt()

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

Functions

req(arg)

(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(arg, opts)

(macro)

request(remote_service, fun, args, opts \\ [])

@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!(remote_service, op, args \\ [], opts \\ [])

@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