Using JavaScript with Tyk
Last updated: 9 minutes read.
Tyk’s JavaScript Virtual Machine (JSVM) provides a serverless compute function that allows for the execution of custom logic directly within the gateway itself. This can be accessed from multiple locations in the API processing chain and allows significant customization and optimization of your request handling.
In this guide we will cover the features and resources available to you when creating custom functions, highlighting where there are limitations for the different middleware stages.
Scripting basics
Here we cover various facets that you need to be aware of when creating custom functions for Tyk.
Accessing external and dynamic data
JS functions can be given access to external data objects relating to the API request. These allow for the modification of both the request itself and the session:
request
: an object describing the API request that invoked the middlewaresession
: the key session object provided by the client when making the API requestconfig
: an object containing fields from the API definition
Note
There are other ways of accessing and editing a session object using the Tyk JavaScript API functions.
Creating a middleware component
Tyk injects a TykJS
namespace into the JSVM, which can be used to initialise a new middleware component. The JS for each middleware component should be in its own *.js
file.
You create a middleware object by calling the TykJS.TykMiddleware.NewMiddleware({})
constructor with an empty object and then initialising it with your function using the NewProcessRequest()
closure syntax. This is where you expose the external data objects to your custom function.
Note
- For Custom JS plugins and Dynamic Event Handlers, the source code filename must match the function name
- Virtual Endpoints do not have this limitation
Returning from the middleware
When returning from the middleware, you provide specific return data depending upon the type of middleware.
Returning from Custom JS plugin
A custom JS plugin can modify fields in the API request and the session metadata, however this is not performed directly within the JSVM so the required updates must be passed out of the JSVM for Tyk to apply the changes. This is a requirement and omitting them can cause the middleware to fail.
The JS function must provide the request
and session.meta_data
objects in the ReturnData
as follows:
return sampleMiddleware.ReturnData(request, session.meta_data);
Custom JS plugins sit in the middleware processing chain and pass the request onto the next middleware before it is proxied to the upstream. If required, however, a custom JS plugin can terminate the request and provide a custom response to the client if you configure the ReturnOverrides
in the request
object, as described here.
Returning from Virtual Endpoint
Unlike custom JS plugins, Virtual Endpoints always terminate the request so have a different method of returning from the JS function.
The function must return a responseObject
. This is crucial as it determines the HTTP response that will be sent back to the client. The structure of this object is defined to ensure that the virtual endpoint can communicate the necessary response details back to the Tyk Gateway, which then forwards it to the client.
The responseObject
has the following structure:
code
: an integer representing the HTTP status code of the responseheaders
: an object containing key-value pairs representing the HTTP headers of the responsebody
: a string that represents the body of the response which can be plain text, JSON, or XML, depending on what your API client expects to receive
You must provide the responseObject
together with the session.meta_data
as parameters in a call to TykJsResponse
as follows:
return TykJsResponse(responseObject, session.meta_data);
You can find some examples of how this works here.
JavaScript resources
JavaScript (JS) functions have access to a system API and library of functions. They can also be given access to certain Tyk data objects relating to the API request.
The system API provides access to resources outside of the JavaScript Virtual Machine sandbox, the ability to make outbound HTTP requests and access to the key management REST API functions.
The request
object
The request
object provides a set of arrays that describe the API request. These can be manipulated and, when changed, will affect the request as it passes through the middleware pipeline. For virtual endpoints the request object has a different structure.
The structure of the request
object is:
class ReturnOverrides {
ResponseCode: number = 200;
ResponseBody: string = "";
ResponseHeaders: string[] = [];
}
class Request {
Headers: { [key: string]: string[] } = {};
SetHeaders: { [key: string]: string } = {};
DeleteHeaders: string[] = [];
Body: string = "";
URL: string = "";
AddParams: { [key: string]: string } = {};
DeleteParams: string[] = [];
ReturnOverrides: ReturnOverrides = new ReturnOverrides();
IgnoreBody: boolean = false;
Method: string = "";
RequestURI: string = "";
Scheme: string = "";
}
Headers
: this is an object of string arrays, and represents the current state of the request header; this object cannot be modified directly, but can be used to read header dataSetHeaders
: this is a key-value map that will be set in the header when the middleware returns the object; existing headers will be overwritten and new headers will be addedDeleteHeaders
: any header name that is in this list will be deleted from the outgoing request; note thatDeleteHeaders
happens beforeSetHeaders
Body
: this represents the body of the request, if you modify this field it will overwrite the requestURL
: this represents the path portion of the outbound URL, you can modify this to redirect a URL to a different upstream pathAddParams
: you can add parameters to your request here, for example internal data headers that are only relevant to your network setupDeleteParams
: these parameters will be removed from the request as they pass through the middleware; noteDeleteParams
happens beforeAddParams
ReturnOverrides
: values stored here are used to stop or halt middleware execution and return an error codeIgnoreBody
: if this parameter is set totrue
, the original request body will be used; if set tofalse
theBody
field will be used (false
is the default behavior)Method
: contains the HTTP method (GET
,POST
, etc.)RequestURI
: contains the request URI, including the query string, e.g./path?key=value
Scheme
: contains the URL scheme, e.g.http
,https
Using ReturnOverrides
If you configure values in request.ReturnOverrides
then Tyk will terminate the request and provide a response to the client when the function completes. The request will not be proxied to the upstream.
The response will use the parameters configured in ReturnOverrides
:
ResponseCode
ResponseBody
ResponseHeaders
In this example, if the condition is met, Tyk will return HTTP 403 Access Denied
with the custom header "X-Error":"the-condition"
:
var testJSVMData = new TykJS.TykMiddleware.NewMiddleware({});
testJSVMData.NewProcessRequest(function(request, session, config) {
// Logic to determine if the request should be overridden
if (someCondition) {
request.ReturnOverrides.response_code = 403;
request.ReturnOverrides.response_body = "Access Denied";
request.ReturnOverrides.headers = {"X-Error": "the-condition"};
// This stops the request from proceeding to the upstream
}
return testJSVMData.ReturnData(request, session.meta_data);
});
The virtual endpoint request
object
For virtual endpoint functions the structure of a Javascript request
object is:
class VirtualEndpointRequest {
Body: string = "";
Headers: { [key: string]: string[] } = {};
Params: { [key: string]: string[] } = {};
Scheme: string = "";
URL: string = "";
}
Body
: HTTP request body, e.g.""
Headers
: HTTP request headers, e.g."Accept": ["*/*"]
Params
: Decoded query and form parameters, e.g.{ "confirm": ["true"], "userId": ["123"] }
Scheme
: The scheme of the URL ( e.g.http
orhttps
)URL
: The full URL of the request, e.g/vendpoint/anything?user_id=123\u0026confirm=true
Note
Each query and form parameter within the request is stored as an array field in the Params
field of the request object.
Repeated parameter assignments are appended to the corresponding array. For example, a request against /vendpoint/anything?user_id[]=123&user_id[]=234
would result in a Javascript request object similar to that shown below:
const httpRequest = {
Headers: {
"Accept": ["*/*"],
"User-Agent": ["curl/8.1.2"]
},
Body: "",
URL: "/vendpoint/anything?user_id[]=123\u0026user_id[]=234",
Params: {
"user_id[]": ["123", "234"]
},
Scheme: "http"
};
The session
object
Tyk uses an internal session object to handle the quota, rate limits, access allowances and auth data of a specific key. JS middleware can be granted access to the session object but there is also the option to disable it as deserialising it into the JSVM is computationally expensive and can add latency. Other than the meta_data
field, the session object itself cannot be directly edited as it is crucial to the correct functioning of Tyk.
Limitations
- Custom JS plugins at the pre- stage do not have access to the session object (as it has not been created yet)
- When scripting for Virtual Endpoints, the
session
data will only be available to the JS function if enabled in the middleware configuration.
Sharing data between middleware using the session
object
For different middleware to be able to transfer data between each other, the session object makes available a meta_data
key/value field that is written back to the session store (and can be retrieved by the middleware down the line) - this data is permanent, and can also be retrieved by the REST API from outside of Tyk using the /tyk/keys/
method.
Note
A new JSVM instance is created for each API that is managed. Consequently, inter-API communication is not possible via shared methods, since they have different bounds. However, it is possible using the session object if a key is shared across APIs.
The config
object
The third Tyk data object that is made available to the script running in the JSVM contains data from the API Definition. This is read-only and cannot be modified by the JS function. The structure of this object is:
APIID
: the unique identifier for the APIOrgID
: the organization identifierconfig_data
: custom attributes defined in the API description
Adding custom attributes to the API Definition
When working with Tyk OAS APIs, you can add custom attributes in the data
object in the x-tyk-api-gateway.middleware.global.pluginConfig
section of the API definition, for example:
|
|
When working with Tyk Classic APIs, you simply add the attributes in the config_data
object in the root of the API definition:
|
|
Underscore.js Library
In addition to our Tyk JavaScript API functions, you also have access to all the functions from the underscore library.
Underscore.js is a JavaScript library that provides a lot of useful functional programming helpers without extending any built-in objects. Underscore provides over 100 functions that support your favorite functional helpers:
- map
- filter
- invoke
There are also more specialized goodies, including:
- function binding
- JavaScript templating
- creating quick indexes
- deep equality testing
Example
In this basic example, we show the creation and initialisation of a middleware object. Note how the three Tyk data objects (request
, session
, config
) are made available to the function and the two objects that are returned from the function (in case the external objects need to be updated).
|
|