REST APIs - FIN5 as a Client
- 1 Introduction
- 2 Prerequisites
- 3 Source code
- 4 OpenHolidays example
- 5 Wrap up
Introduction
This document provides some practical example usage of how to implement REST APIs in FIN, using Fantom programming language.
Prerequisites
Understanding of what REST APIs are
Knowledge of at least one programming language and software development in general (Fantom is recommended)
Knowledge of how FIN works
what is a pod / extension - https://fantom.org/doc/docLang/Pods
how to create a custom pod - https://www.fantom.org/doc/docTools/Build
how to use Folio and call Axon functions - Folio
Haxhall docs - what is a Diff, how to commit - func:diff
Source code
The full source code for this example is contained in the following ZIP file.
OpenHolidays example
OpenHolidays API is a small Open Data project that collects public holiday and school holiday data and makes it available via an open REST API interface.
The following example will showcase how to:
read data from the OpenHolidays endpoints, to get the holidays for a specific city
use this data to build a scheduler: we want, for example, to set to "off" or false a set of points on scheduled holidays
Disclaimer
Do not use this architecture / structure to update the curVal
of points. To do that, you should write a custom connector.
WebClient and basic HTTP utilities
In fan/http
you can find the Http.fan
utility class that contains two functions that allow getting and posting JSON to a REST endpoint.
Getting JSON
Let's start from the GET function.
The function accepts two parameters:
the url of the endpoint to call
a
Dict
where you can specify custom headers: this is particularly useful when you want to add authentication headers, for example
What this function does is:
instantiating a WebClient that points to the URL to invoke
setting the request method to "GET"
setting the HTTP headers
executing the request
reading the response code to understand the outcome of the request
parsing the response stream to JSON: this will return a map or a list of maps
Str:Obj
Since we don't know if the API will return an object or an array, the result type of this function is Obj?
. Returning an array is not a good practice in REST API development, but there are many implementations that still do it. It's responsability of who is using this function to properly cast the returned object (we will see an example later).
This should all look familiar to anyone who had to develop a REST API.
Posting JSON
Let's see how the POST works. It accepts one more parameter, which will be a String representing the JSON body of the request.
The POST function is very similar to the GET function. It adds some additional standard headers, like "Content-Type: application/json", and "Content-Length": the length of the body is necessary for the WebClient to properly send out the request body.
Then, the body of the request is written and the request is sent out.
As before, we can then read the JSON response.
So, Http.fan
contains two generic example functions to GET and POST data to an external endpoint. We can write similar functions for the other operations (PUT, PATCH and DELETE).
OpenHolidaysClient
OpenHolidaysClient.fan
is a class that acts as a REST client for the OpenHolidays APIs. It contains the implementation of that specific APIs, making use of the Http.fan
utilities to get the data.
It implements two specific APIs of OpenHolidays:
getSubdivisions
: given a country as a 2-letter ISO country code, returns the subdivisions of that country, that are regions/cities. Each subdivision is identified by a code. We will use this API to find the subdivision code that represents the city where our FIN site is placed.
getPublicHolidays
: given a country as a 2-letter ISO country code and a timerange, returns the list of public holidays applicable. Each holiday can be National or Local. If it's Local, it's only applicable for a specific subdivision.
The other functions in the class are just utility functions to search specific data in the API result.
Let's see one operation in detail:
What this function does is:
Build the URL by specifying the query parameters to filter the data; the resulting URL will be something like: https://openholidaysapi.org/Subdivisions?countryIsoCode=IT&languageIsoCode=EN
languageIsoCode: is the language to use to describe the subdivisions
countryIsoCode: is the 2-letter ISO country code
Execute the GET call and cast the result as an array of maps
[Str:Obj][]
Mapping the resulting JSON (array of maps) into an object/class; see the next section
Returning the mapped objects as a result
Mapping JSON to objects / classes
The result of the GET/POST operations is a map, or an array of maps. Wouldn't it be nice to have an object instead? We can declare a class like Subdivision.fan
and then map the JSON data into the constructor, to build a new object instance:
The parameter of the constructor is the JSON map. Then we use an utility function, declared in HttpUtils.fan
, that makes use of the reflection to map the JSON data to this object.
In this way the data structure is clear and documented, and you can easily access the data by using field accessors.
At this point we have the APIs implemented, and we can now use the returned data to achieve our goal: create a holidays schedule.
Creating a schedule point
A schedule point is a record with specific tags, see scheduleExt
.
The most important tag of a schedule record is the scheduleGrid
, because it contains the schedule events and rules. An example of schedule grid is:
So each event has a date and timerange, a description, and a value. val
is the value that will be written to the points bound to this scheduler, and can be of any type (string, number, boolean).
Let's see how to create a schedule point:
You can create a new record simply by committing a diff (more details on the linked documentation).
OpenHolidaysService: putting the pieces together
The OpenHolidaysService.fan
is a service class that connects the pieces together: querying the API, transforming the data into schedule events, and creating a schedule record.
The first function accepts as parameters:
the timerange of the holidays to query
the id of a site
the value that the schedule should apply to the points ("val")
the current Context, which is an optional parameter (defaults to the current execution Context - it is used to identify the current project and user executing the operations)
The function reads the site to get the "geoCountry" and "geoCity" tags; these two tags will be used as the "isoCountryCode" and as a filter to identify the correct subdivision of the holidays APIs.
Then it GETs the public holidays from the APIs, and map them to a schedule grid. Finally, it creates a schedule point. The point can then be viewed using the Schedules app of FIN.
There is just one last missing piece: how can we call this function?
Exposing Fantom functions as Axon functions
Functions and methods created in a Fantom pod can be exposed as Axon functions, that can be called using Folio.
You just need to use the @Axon
annotation and add the function to the Lib
class of the pod:
This Lib class is a special class that is referenced in the index
section of the build.fan
, and it is mainly used to expose Fantom functions as Axon functions.
Final test
Now we can do a final test and see how to use the functions we developed.
After compiling the pod and restarting FIN, we can create a new site and set the geoCountry and geoCity tags:
Click on the "i" icon in the top right corner to open the tags view and copy the id of the site.
Then we can open Folio and call the Axon function we exposed in the Lib class:
Example:
testCreateSiteHolidaysSchedule(2025-01-01..2025-12-31, @p:demo:r:2f24d1c7-252b8b94, "off")
Replace @p:demo:r:2f24d1c7-252b8b94
with the id of your site. You can also adjust the timerange (2025-01-01..2025-12-31
) and the schedule point value ("off"
) to whatever fits your needs.
If the function call is successful, we can then open the Schedules app and see our newly created schedule point with all the events:
Example usage: a job
To complete the use case, we can imagine to create a job that once a year automatically creates the schedule for the site.
First we need a new Axon function to call from the job, that will simply calculate the date span for the next year, based on today’s date:
Now open the “Jobs” app in FIN and click on “Create New”
Use the following configuration to create a new job that runs once a year and creates the schedule for the next year
you can then click on “Run” to run the job for the first time
If you want this function to run on a specific day (example: the 31st of December), you should use Tasks combined with a Scheduler Observable.
Wrap up
This is an example of how to build a REST client in Fantom, implement some APIs, transform and use the data obtained. There is much more that can be done. The suggestion is to consult the documentation of the Fantom language and the Skyspark docs:
http://20.49.58.255:8085/ui/ - Get in touch with Tech Support if you don't have access to this portal.