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
endCore Functions
request/4- Make a structured request to a remote service.request!/4- Same asrequest/4but raises on error.req/1,req/2- Macro versions for cleaner syntax.
Request Flow
- Creates "request-out" span for tracing
- Calls
malla_request/3callback (interceptable by plugins) - Uses
Malla.remote/4for RPC to target service - Remote side creates "request-in" span
- Executes target function
- Normalizes response via
Malla.Status - 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 viaMalla.Status){:status, term}- Custom status (normalized viaMalla.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
Malla.Plugins.Request- Server-side request handling and plugin interfaceMalla.Node- Lower-level RPC functionsMalla.Status- Response normalization
Summary
Types
@type op() :: [:atom] | String.t()
@type req_opt() :: Malla.remote_opt() | {:direct, boolean()}
Functions
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)
@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_requestin 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
directis used, the request is called locally, skipping the call toscallb/4 - Remote request is expected to return specific responses
@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