Open Source API Gateway

Built-in Variables for Groovy, JavaScript and SpEL

Groovy and JavaScript scripts and SpEL expressions in Membrane API Gateway get the current exchange and its messages as ready-to-use built-in variables. The names are the same in all three languages, but the way to access a value differs. Functions like user() or jsonPath() are listed in Built-in Functions for Groovy, JavaScript and SpEL.

LanguageUsed in
Groovygroovy, template
JavaScriptjavascript
SpEL${...} in setHeader, setProperty, if, log, ...

Overview

The request POST /shop/tea?limit=10 with the header X-Customer: 42, the cookie session=abc, the body {"name":"Oolong"} and the API path /shop/{category} gives:

VariableGroovyJavaScriptSpELValue
headerheader['X-Customer']header.get('X-Customer')${header['X-Customer']}42
cookiecookie.sessioncookie.get('session')${cookie.session}abc
paramparam.limit[0]params.get('limit')[0]${param.limit[0]}10
pathParampathParam.categorypathParam.get('category')${pathParam.category}tea
jsonjson.namejson.name${json.name}Oolong
bodybody.toString()body.toString()${body}{"name":"Oolong"}
propertyproperty.tenantproperty.get('tenant')${property.tenant}value of the property
flowflowflow${flow}REQUEST
methodmethodmethod${method}POST
pathpathpath${path}/shop/tea?limit=10
exchangeexchange.request.uriexchange.getRequest().getUri()${exchange.request.uri}/shop/tea?limit=10

In the response flow:

VariableGroovyJavaScriptSpELValue
statusCodestatusCodestatusCode${statusCode}200
headerheader['Content-Type']header.get('Content-Type')${header['Content-Type']}application/json

Language Differences

  • JavaScript accesses Java maps through their methods: header.get('X-Customer'), property.put('tenant', 'demo'). Index or dot access like header['X-Customer'] returns undefined. Only json is a JavaScript object and is read with json.name.
  • SpEL needs brackets for names containing -: ${header['X-Customer']}. ${header.X-Customer} is read as a subtraction and fails.
  • SpEL returns a list for a query parameter as [10]. Use ${param.limit[0]} for the value.

Which Message?

message, header, cookie, body and json refer to the message of the current flow: the request in the request flow, the response in the response flow.

VariableGroovy, JavaScriptSpEL
param / paramsrequest flow onlyboth flows
responseresponse flow onlywhen a response exists
statusCoderesponse flow onlywhen a response exists

Put a plugin under request: or response: to run it in one flow only. A plugin directly in flow: runs in both.

All Variables

VariableGroovyJSSpELDescription
flow✓✓✓REQUEST, RESPONSE or ABORT
method✓✓✓HTTP method of the request
path✓✓✓Request URI including the query string
header / headers✓✓✓Headers of the current message
cookie / cookies✓✓✓Cookies of the current message, read-only
param / params✓✓✓Query parameters, each a list of values
pathParam✓✓✓Path parameters from a path: uri: /shop/{category} template, read-only
json✓✓✓Parsed JSON body of the current message
body✓✓✓Body of the current message, converts to a string
property / properties✓✓✓Exchange properties, shared by all plugins of the exchange
props✓✓Alias for property
message✓✓✓Current message: the request or the response
request✓✓✓The request, in both flows
response✓✓✓The response
statusCode✓✓✓Status code of the response
exchange✓✓✓The whole exchange: request, response, properties
exc✓✓Alias for exchange
gateway✓${gateway.version}: Membrane version
CONTINUE, RETURN, ABORT✓✓Return values that steer the flow, see Return Values
spring, registry✓✓Access to beans defined in the configuration

In Groovy and JavaScript, request and response are the full Request and Response objects. In SpEL, they only offer header, body and version: ${request.header['X-Customer']}.

Details

header

// Groovy
def customer = header['X-Customer'] // null if missing
header.put('X-Gateway', 'Membrane') // replaces existing values
header.remove('X-Internal')
// Groovy
def customer = header['X-Customer']     // null if missing
header.put('X-Gateway', 'Membrane')     // replaces existing values
header.remove('X-Internal')
// JavaScript
var customer = header.get('X-Customer');
header.put('X-Gateway', 'Membrane');
// JavaScript
var customer = header.get('X-Customer');
header.put('X-Gateway', 'Membrane');

Header names are case-insensitive. A header that occurs more than once returns its values joined with ,: two X-Multi headers with a and b give "a, b".

cookie

The map is parsed from the Cookie header. Changing it does not change the message. To set a cookie, write the header: header.put('Set-Cookie', 'session=abc; HttpOnly').

param

// curl "localhost:2000?limit=10&tag=a&tag=b"
def limit = param.limit?.getAt(0) // "10", or null if missing
def tags = param.tag // ["a", "b"]
// curl "localhost:2000?limit=10&tag=a&tag=b"
def limit = param.limit?.getAt(0)       // "10", or null if missing
def tags  = param.tag                   // ["a", "b"]

Each parameter maps to a list, because a parameter can occur more than once.

json

json only exists when the body of the current message is valid JSON with a JSON content type. Otherwise a script fails with No such property: json. Check first if the body may be something else:

if (binding.hasVariable('json')) { ... }
if (binding.hasVariable('json')) { ... }
- if:
test: isJSON()
flow:
- setHeader:
name: X-Name
value: ${json.name}
- if:
    test: isJSON()
    flow:
      - setHeader:
          name: X-Name
          value: ${json.name}

property

property.customer = json.customer       // Groovy: set
property.customer = json.customer       // Groovy: set
property.put('customer', json.customer); // JavaScript: set
property.put('customer', json.customer); // JavaScript: set
- setHeader:
name: X-Customer
value: ${property.customer} # SpEL: read
- setHeader:
    name: X-Customer
    value: ${property.customer}         # SpEL: read

Properties live as long as the exchange. Use them to pass values between plugins, e.g. from a script in the request flow to one in the response flow.

In template, use property instead of properties: there, properties returns the properties of the template object itself.

Return Values

Only for Groovy and JavaScript scripts. The value of the last expression decides what happens next:

Return valueEffect
CONTINUEThe flow continues with the next plugin
RETURNThe flow turns around; a response must already exist
ABORTThe flow is aborted
ResponseBecomes the response, the flow returns
RequestReplaces the request
Map, JavaScript objectBecomes the JSON body of the current message
String, byte[]Becomes the body of the current message
nullThe flow continues unchanged

End a script with CONTINUE when it only reads or changes values. Otherwise the last expression may be used as the new body by accident. For example, header.put(...) returns the previous header value, a String.

header.put('X-Checked', 'true')
CONTINUE
header.put('X-Checked', 'true')
CONTINUE

For more examples, see the transformation tutorials and the scripting overview.