Guide
Integrations are ready-made services that make it simple to configure and customize specific processes. You can create your own Integration templates and sync data from any external system.
Fibery has a lot of built-in integrations like Jira, Trello, Github, etc. but sometimes the need arises to integrate custom data. In this article, we will show how to create simple integration app which does not require any authentication.
Let's imagine we intend to create a public holidays app which will sync data about holidays for selected countries. The holidays service https://date.nager.at will be used to retrieve holidays.
Please keep in mind that Type = Database, App = Space. To find out why, check Terminology.
Getting Started
All communication between an integration application and Fibery services is done via standard hypertext protocols, whether that be HTTP or HTTPS. Please check out the full documentation starting from Integrations API.
The choice of underlying technologies used to develop integration applications is up to the individual developer. We are going to implement all required endpoints in web app step by step. We will use Node.js for implementing this integration app.
The source code can be found here.
App configuration endpoint
Every integration should have the configuration which describes what the app is doing and the authentication methods.
The app configuration should be accessible at GET "/" endpoint and should be publicly available. For example, we used Heroku to host the app.
This is the endpoint implementation.
const appConfig = require(`./config.app.json`);
app.get(`/`, (req, res) => res.json(appConfig));
This is how config.app.json looks like.
{
"id": "holidays-app",
"name": "Public Holidays",
"version": "1.0.1",
"description": "Integrate data about public holidays into Fibery",
"authentication": [
{
"id": "public",
"name": "Public Access",
"description": "There is no any authentication required",
"fields": []
}
],
"sources": [],
"responsibleFor": {
"dataSynchronization": true
}
}
All properties are required. Find the information about all properties here.
Since we don't want my app be authenticated, we didn't provide any fields for "Public Access" node in authentication. It means that any user will be able to connect their account to the public holidays app. Find an example with token authentication here.
Validate
This endpoint is responsible for app account validation. It is required to be implemented. Let's just send back the name of account without any authentication since we are creating an app with public access.
POST /validate
app.post(`/validate`, (req, res) => res.json({name: `Public`}));
Sync configuration endpoint
The way data is synchronised should be described.
The endpoint is POST /api/v1/synchronizer/config
const syncConfig = require(`./config.sync.json`);
app.post(`/api/v1/synchronizer/config`, (req, res) => res.json(syncConfig));
config.sync.json
{
"types": [
{
"id": "holiday",
"name": "Public Holiday"
}
],
"filters": [
{
"id": "countries",
"title": "Countries",
"datalist": true,
"optional": false,
"type": "multidropdown"
},
{
"id": "from",
"type": "number",
"title": "Start Year (by default previous year used)",
"optional": true
},
{
"id": "to",
"type": "number",
"title": "End Year (by default current year used)",
"optional": true
}
]
}
The types are responsible for describing types which will be synced. For the holidays app it is just one type with id "holidays" and name "Public Holidays". It means that only one integration Fibery database will be created in the space, with the name "Public Holidays".
The filters contain information on how the type can be filtered. In our case, there is a multi drop down ('countries') which is required and marked as data list. It means that options for this drop down should be retrieved from app and special end-point should be implemented for that. Also, we have two numeric filters from and to which are optional and can be used to filter holidays by years .
Datalist
Endpoint POST /api/v1/synchronizer/datalist should be implemented if synchronizer filters has dropdown marked as "datalist": true.
Since we have countries multi drop down which should contain countries it is required to implement the mentioned endpoint as well.
app.post(`/api/v1/synchronizer/datalist`, wrap(async (req, res) => {
const countries = await (got(`https://date.nager.at/api/v3/AvailableCountries`).json());
const items = countries.map((row) => ({title: row.name, value: row.countryCode}));
res.json({items});
}));
For this app, only the list of countries is returned since our config has only one data list. In the case where there are several data lists then we will need to retrieve "field" from request body which will contain an id of the requested list. The response should be formed as an array of items where every element contains title and value properties.
For example, part of countries response will look like this:
{ "items": [
{ "title": "Poland", "value": "PL"},
{ "title": "Belarus", "value": "BY"},
{ "title": "Cyprus", "value": "CY"},
{ "title": "Denmark", "value": "DK"},
{ "title": "Russia", "value": "RU"}
]}
Schema
POST /api/v1/synchronizer/schema endpoint should return the data schema of the app. In our case it should contain only one root element "holiday" named after the id of holiday type in sync configuration above.
const schema = require(`./schema.json`);
app.post(`/api/v1/synchronizer/schema`, (req, res) => res.json(schema));
schema.json content can be found below
{
"holiday": {
"id": {
"name": "Id",
"type": "id"
},
"name": {
"name": "Name",
"type": "text"
},
"date": {
"name": "Date",
"type": "date"
},
"countryCode": {
"name": "Country Code",
"type": "text"
}
}
}
NOTE: Every schema type should have "id" and "name" defined.
Data
The data endpoint is responsible for retrieving data. Check the documentation on how request body looks. There is no paging needed in case of our app, so the data is returned according to selected countries and years interval. The source code can be found here.
POST /api/v1/synchronizer/data
app.post(`/api/v1/synchronizer/data`, wrap(async (req, res) => {
const {requestedType, filter} = req.body;
if (requestedType !== `holiday`) {
throw new Error(`Only holidays database can be synchronized`);
}
if (_.isEmpty(filter.countries)) {
throw new Error(`Countries filter should be specified`);
}
const {countries} = filter;
const yearRange = getYearRange(filter);
const items = [];
for (const country of countries) {
for (const year of yearRange) {
const url = `https://date.nager.at/api/v3/PublicHolidays/${year}/${country}`;
console.log(url);
(await (got(url).json())).forEach((item) => {
item.id = uuid(JSON.stringify(item));
items.push(item);
});
}
}
return res.json({items});
}));
The requestedType and filter can be retrieved from the request body.
The response should be returned as array in "items" element.
{ "items": [] }
Testing custom integration app
It is recommended to create integration tests before adding your custom app to Fibery apps gallery. Check some tests I created for holidays app here.
It is possible to run your app on local machine and make the app's url publicly available by using tools like ngrok.
brew install ngrok
ngrok http 8080
Then you will have an ability to debug the app locally after adding it Fibery apps gallery. Don't forget to remove the app from Fibery integration apps catalog after testing.
Find the source code of this app and other examples in our public gitlab repository.
An integration sync could cleanup (delete) entities which is potentially quite dangerous, since restoration is not super easy. However, integration entities with ANY schema modifications will not be deleted during sync.
This applies if:
To allow users to manage source-deleted entities, there is a Simple Text field deleted in source with values yes and no. If an entity is deleted in the source, the value will be set to yes. Admins can then manually delete these entities if needed (manually or via automation).
FAQ
Where can I ask questions regarding API & Integrations?
The best place is to ping us in chat or in our community.
Is there any way to pass batches of entity data to an external action?
No, there are no batch actions available
However, batch actions can be implemented in a custom app by accumulating incoming items into app storage and performing actions on them as a batch.
Please, note that the Automation API calls custom actions for Entities one by one in queue. That means, that the whole execution will be stopped if the first action fails.
Community examples: