Building APIs
Basics
Any Snapi expression can be turned into an API endpoint.
The simplest expressions like 1+1
can be used to generate an output.
It needs to be set in a function, like in:
foo() = 1+1
Of course, things more useful can be done with expressions. For instance if we want to check which products are low on stock from a MySQL table, we would create something like the following:
lowstock() =
let
data = MySQL.InferAndRead("mysql001", "products")
in
Collection.Filter(data, x -> x.stockcount < 5)
Once a function is available in a code file, we can turn it into a REST API endpoint by describing it in a YML file.
raw: '{{version}}'
endpoint: GET
code: snapi
codeFile: stock.snapi
declaration: lowstock
format: json
computeClass: normal
security:
public: false
This YML file describes an HTTP GET endpoint named stockalert
(the file name becomes the endpoint name) which is
mapped to a Snapi function named lowstock
located in a file named stock.snapi
A Snapi code file can contain multiple functions and they can all be published as endpoints. It is also possible to create multiple YML files for the same function.
Endpoint, base path and route
Let's have a look at this URI:
https://dev-01.raw-labs.com/acme/v1/private/stockalert
https://dev-01.raw-labs.com/
is the base path (URL) and acme/v1/private/stockalert
is the route.
An endpoint represents an action which returns data; in other terms, it is the HTTP GET operation on the
URI https://dev-01.raw-labs.com/acme/v1/private/stockalert
dev-01
is the RAW subdomain assigned to the account being used.
The route is composed with three things:
{publishURL}/{GitHub repository relative directory}/{YML file name without extension}
In our example, {publishURL} is 'acme'.This value is set globally in the raw-site.yml file located in the GitHub repository root folder. Typically, this could be the name of a partner, supplier or team you want to share data with; or the stock data sharing project as a whole.
{GitHub repository relative directory} is the GitHub folder where the YML file describing the endpoint is located.
This could be setup the following way in GitHub:
github.com/raw-labs => GitHub account
│ README.md
└───stock-project => RAW enabled API repository
| | raw-site.yml => API global parameters
│ └───V1
│ └───private => V1/private : relative directory
│ │ stock.snapi => code
│ │ stockalert.yml => endpoint description
│ │ ...
│
└───other-stuff
│ ...
Deployment
Expressions go into functions which are mapped into endpoints with a pair of files: the code file and the YML description file. Whenever the "git push" button is hit and these files get uploaded on the RAW enabled API repository, the deployment process will start.
none of the API endpoints will go live. It is all or nothing.
The deployment time may vary, depending on the number of endpoints, files, code lentgh and complexity.
The API deployment status can be monitored in the Producer administration web interface, under the Monitoring tab.
In this list, the state column can take three different values: "IN PROGRESS", "FAILED" or "SUCCESS". In case of errors, detailed information is available by clicking the Messages or Branches views on the right.
Disabling or removing an Endpoint
Disabling an endpoint is achieved by setting the enabled
field to false
in its YAML file and committing the change to GitHub. The RAW repository reader app will notice the change and disable the endpoint.
Removing an endpoint is done by deleting the {endpoint}.yml file and committing the change to the GitHub repository.
Handling query parameters
Parameters in functions
It is possible to pass parameters to functions. This is done through the following syntax:
hello(name: string) = "Hello " + name + "!"
Arguments can have default values:
hello(name: string = "Anonymous") = "Hello " + name + "!"
Endpoint query parameters
Published endpoints are mapped to functions. The function declaration will determine the endpoint behavior regarding parameters, the rules applying to the function will apply similarly to endpoints. For instance:
// Mandatory => .../hello?name="Joe"
hello(name: string) = ...
// Optionnal => .../hello (will use "Anonymous") or .../hello?name="Joe"
hello(name: string = "Anonymous") = ...