hefestoapicontainer

Endpoints y flow de las directivas

Declaración de endpoints

Para declarar endpoints en cualquier API de Hefesto solo tienes que añadirlos al api.yaml, dentro de la sección endpoints. Cada endpoint se define con el verbo HTTP seguido del path:

endpoints:
  get /user/{id}:
    Ping:
      directive: Ping
  post /user:
    Ping:
      directive: Ping

En el ejemplo anterior hemos declarado dos endpoints: un GET a /user/{id} y un POST a /user. Ambos ejecutan una sola directiva llamada Ping, que devuelve un JSON con el mensaje "pong".

Los path params se definen con llaves {id}, y desde las directivas o desde el api.yaml se accede a ellos mediante alias: $.message.pathParam.id.

Estructura completa de api.yaml

Un fichero api.yaml de una API tiene tres secciones principales: before, endpoints y after.

key: rutas-en-bici
before:
  CacheDisabled:
    directive: CacheDisabled
  PropagateCorrelationId:
    directive: PropagateCorrelationId
after: 
  OnError:
    directive: OnError
    groups:
      - ERROR_FLOW
  Log:
    directive: Log
    groups:
      - AFTER_FLOW
endpoints:
  get /:
    RateLimit:
      directive: RateLimit
      path: get_home
      dayTotal: 80000
      dayIp: 10000
      minuteTotal: 400
      minuteIp: 45
    GetProvinces:
      directive: Pull
      host: $.memory.hefesto-localhost
      path: /geo-bike-routes/provinces
      target: provinces
      cache: 31536000
    View:
      directive: View
      name: home
      staticBasePath: $.map.main.staticBasePath
      css: 
        - bootstrap.min
      js: 
        - bootstrap.bundle.min
        - Search
        - home
      fragments:
        - footer
      data:
        provinces: $.memory.provinces

Flujo de ejecución

Al recibir una petición HTTP, las directivas se ejecutan en este orden:

  1. Before: se ejecutan las directivas definidas en before:. Se suelen usar para tareas transversales que aplican a todos los endpoints: CacheDisabled, PropagateCorrelationId, RateLimit, Authenticate, CheckKey, etc.
  2. Endpoint: se ejecutan las directivas del endpoint concreto que ha recibido la petición, en el orden en que aparecen.
  3. After: se ejecutan las directivas definidas en after:, respetando sus grupos. Aquí suele ir OnError (grupo ERROR_FLOW) y Log (grupo AFTER_FLOW).

Grupos (groups)

Los grupos alteran el flujo normal de ejecución. Cada directiva puede pertenecer a uno o varios grupos:

También puedes crear grupos personalizados y activarlos/desactivarlos desde el estado:

$state->groups()->enable('CUSTOM_NAME');
$state->groups()->disable('ANOTHER_NAME');
$state->groups()->disableAll();

Ejemplo de grupos personalizados:

post /grid:
  SetGridFlow:
    directive: SetGridFlow
  CalculateGrid:
    directive: CalculateGrid
    groups:
      - NORMAL_FLOW
      - DIGESTION_REQUEST_FLOW
  Connect:
    directive: DatabaseConnect
    groups:
      - DIGESTION_REQUEST_FLOW
  SaveGrid:
    directive: SaveGrid
    groups:
      - DIGESTION_REQUEST_FLOW
  Push:
    directive: Push
    path: /jobs/http/digestion
    groups:
      - DIGESTION_REQUEST_FLOW
  PrepareResponse:
    directive: ModifyMessage
    body: $.memory.response

Comportamiento según grupos — Ejemplo completo

key: mi-api
before:
  CheckKey:
    directive: CheckKey
    expected: $.map.main.key
    current: $.message.header.api-key
after: 
  OnError:
    directive: OnError
    groups:
      - ERROR_FLOW
  Log:
    directive: Log
    groups:
      - AFTER_FLOW
endpoints:
  get /user/{id}:
    Connect:
      directive: DatabaseConnect
    ReadUser:
      directive: ReadUser
      id: $.message.pathParams.id
    PrepareResponse:
      directive: ModifyMessage
      headers: 
        Content-Type: application/json
      body: $.memory.user

En una llamada contra GET /user/{id} donde no ocurra ningún error, el flujo será:

  1. CheckKey (before)
  2. Connect, ReadUser, ModifyMessage (endpoint)
  3. OnError → no se ejecuta (está en ERROR_FLOW)
  4. Log → se ejecuta en AFTER_FLOW, una vez enviada la respuesta

Si se produce un error en ReadUser, se lanza una excepción. Las directivas ModifyMessage y siguientes de NORMAL_FLOW no se ejecutan. OnError captura el error (porque está en ERROR_FLOW) y Log registra el fallo (porque AFTER_FLOW siempre se ejecuta).

Endpoint especial: all /requests

El endpoint all /requests captura todas las peticiones que no hayan sido resueltas por ningún otro endpoint. Es muy útil para dos casos:

Ejemplo de proxy total:

all /requests:
  Smart:
    directive: Smart
  Http:
    directive: Http
    host: $.memory.poems-host
  CacheStatic:
    directive: CacheStatic
    expiresMinutes: 6000

Ejemplo de 404 con vistas:

all /requests:
  SetSmartError:
    directive: SetSmartError
  RateLimit:
    directive: RateLimit
    path: all_requests
    dayTotal: 80000
    dayIp: 10000
    minuteTotal: 400
    minuteIp: 45
  ThrowError:
    directive: ThrowError
    status: 404
    message: "Not Found

Puedes combinar all /requests con el resto de endpoints: si una petición no es resuelta por ningún endpoint específico, la captura all /requests para dar una respuesta por defecto.

Uso de parámetros dinámicos

Las directivas reciben parámetros desde el api.yaml mediante alias: valores fijos, de maps, de memory o del message.

get /route/{routeId}:
  CalculatePath:
    directive: Concat
    element1: /geo-bike-routes/route/
    element2: $.message.pathParam.routeId
    target: routePath
  Pull:
    directive: Pull
    host: $.memory.hefesto-localhost
    path: $.memory.routePath
    target: route
    queryParams:
      validated: 'true'
      excludingVias: 'true'

En este ejemplo se construye dinámicamente el path a partir del routeId recibido en la URL y se hace una llamada local a otra API.