The walk-through on this page will help you create your first expression-backed record. Expression-backed records differ from other types of record types in that they use web services as their source data.
Use the provided API to understand how the configurations work. Then, try it with your own data or API. Keep in mind, the final configurations will need to change to fit your data type's field names and types.
For this tutorial, you will be required to create several objects in your environment. If you do not have permissions to do so, speak with your system administrator. Also, you will need to create these object in an application. If you have a sandbox application available, you can build object in there. Similarly, you can follow the Application Building Tutorial and create object within that.
The content below assumes a basic familiarity with interfaces and focuses more on the specifics of configuring the data source and list view. Consider going through the Interface Tutorial and Process-Backed and Entity-Backed Record Tutorial. Additionally, Record Design and Create a Record Type pages provide more consideration and how-to examples related to record creation.
The content below also assumes that you have a basic familiarity with using APIs.
We will be using the r/SpaceX API. This is a freely available, open source API that uses JSON and doesn't require authentication.
You'll create the following design objects:
Don't worry - building an expression-backed record isn't rocket science!
Before creating the record type, we will need to create a Data Type, a Connected System, a Record List Source, and a Record View Source.
The first thing we will set up is the CDT for our Record Type. The CDT should contain fields that will come back from your source integrations. These will become the record fields that we can use in our record list, record views, and record actions. For this example, we will create two data types: SpaceX_Launch and SpaceX_Rocket.
First we will create the SpaceX_Rocket data type:
:spacex
to the end of the default namespace.SpaceX_Rocket
. Optionally add a descriptionOnce SpaceX_Rocket has been created, we can create the SpaceX_Launch data type:
:spacex
to the end of the default namespace.SpaceX_Launch
. Optionally add a descriptionWhen creating your CDT, follow the order above. This will ensure the columns in your list view matches what's are presented later in this example.
Once our Data Types have been set up, the next thing we will set up is the connected system for this API. To create a connected system:
r/SpaceX API
.
https://api.spacexdata.com/v3/
.None
.Now that our connected system is set up, we can create our Record List Source. First, we will create an integration, and then we will create an expression to wrap it.
The first integration we build will populate the record list. This integration should return a list of the objects that will map to the Data Type for your record type.
We will be using the launches
endpoint to return an array of SpaceX launches.
r/SpaceX API
connected system.SpaceX_getAllLaunches
.
SpaceX Rules
.launches
. This will be appended to the Base URL of the connected system to form a URL of https://api.spacexdata.com/v3/launches
.
The next thing we want to do is configure our integration to handle paging, searching, and filtering. These can be manifested in a number of ways, depending on the API. The r/SpaceX API uses query parameters.
In this tutorial, we will enable paging by using the offset
, limit
, sort
, and order
parameters. We will enable search to work using the rocket_name
parameter. Finally, we will enable filtering by launch year and launch success, using the launch_year
and launch_success
parameters.
These query parameters have dynamic values, and should be configured as expressions.
pagingInfo
of type PagingInfo, a rule input called searchText
of type Text, a rule input called launch_year
of type Text, and a rule input called launch_success
of type Boolean.
ri!pagingInfo.startIndex - 1
ri!pagingInfo.batchSize
index(ri!pagingInfo.sort, "field", null)
if(index(ri!pagingInfo.sort, "ascending", null), "asc", "desc")
ri!searchText
.ri!launch_year
and the value for launch_success to ri!launch_success
.
ri!searchText and any rule inputs being used for filtering can have null values. However, a configured expression-backed record will never have a null value for startIndex or batchSize. Therefore, test values should be set such that ri!pagingInfo.startIndex and ri!pagingInfo.batchSize are not null.
Now that the integration which backs our record list is set, we need to create an expression to wrap it. This expression will act as the glue between the integration and the record type. It will call the integration and transform it into a DataSubset.
SpaceX_source_getAllLaunches
SpaceX Rules
pagingInfo
of type PagingInfo, a rule input called searchText
of type Text, a rule input called launch_year
of type Text, and a rule input called launch_success
of type Boolean.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
a!localVariables(
/*Call the integration using the available rule inputs*/
local!integration:
rule!SpaceX_getAllLaunches(
pagingInfo: ri!pagingInfo,
searchText: ri!searchText,
launch_year: ri!launch_year,
launch_success: ri!launch_success
),
/*Transform the integration result into a DataSubset*/
a!dataSubset(
startIndex: ri!pagingInfo.startIndex,
batchSize: ri!pagingInfo.batchSize,
sort: ri!pagingInfo.sort,
/*Use the totalCount returned by the response*/
totalCount: tointeger(tointeger(local!integration.result.headers.'Spacex-Api-Count'),
/*Use the list of launches for the record list data*/
data: local!integration.result.body,
/*If any launches were returned, use the flight number as the identifier*/
identifiers: if(
tointeger(local!integration.result.headers.'Spacex-Api-Count') > 0,
local!integration.result.body.flight_number,
{}
)
)
)
Now we've configured our Record List Source, we can create our Record View Source. First, we will create an integration, and then we will create an expression wrap it.
The second integration we build will populate the record views. This integration should return a single object that will map to the Data Type for your record type.
We will be using the launches/{{flight_number}}
endpoint to return a single SpaceX launch.
r/SpaceX API
connected system.SpaceX_getOneLaunch
.
SpaceX Rules
id
of type Number (Integer).1
."launches/"&ri!id
. This will be appended to the Base URL of the connected system to form a URL of "https://api.spacexdata.com/v3/launches/1
, given our test value of 1
for ri!id.
1
for ri!id, this should return a success message:
Now that our second integration is configured to back our record view, we once again need to create an expression to wrap it. This expression will act as the glue between the integration and the record type. It will call the integration and transform it into an Appian dictionary.
SpaceX_source_getOneLaunch
SpaceX Rules
id
of type Number (Integer).1
rule!SpaceX_GetOneLaunch(id: ri!id).result.body
1
for ri!id and clicking Test.Now that we have all of our setup complete, let's create and configure our new record type.
We will take a step-wise approach to configuring the record type, starting with source configuration, then setting up filters, followed by the record list, and finally the record type's summary view.
To begin:
Three things are needed when configuring a record type to accept expressions: a data type, a list view source expression, and a record view source expression. To access these fields, click on the Source & Default Filters page. From the Source dropdown select Expression.
Once expression is selected you will see the fields to define the data source.
SpaceX_source_getAllLaunches
.
pagingInfo
if it was not automatically selected.searchText
if it was not automatically selected.Now, we've mapped the record type Paging Info and Search Text to our Record List Source. These values will be passed through the selected expression rule, into the integration, and ultimately through the query parameters to the r/SpaceX API.
At this point, we have a fully functioning Record List that can page and search. You can view this in Tempo by clicking the View Record List shortcut by the Record Type name.
We can test paging by using the paging controls at the bottom of the record list grid, as well as by sorting on grid columns.
We can test searching by entering different values into the search bar and verifying that they return different lists. This API requires an exact match for the rocket_name
parameter, so try Falcon Heavy
and Falcon 9
.
The initial value for Paging Info has a startIndex of 1. The batchSize and sort are determined by the configuration options on the Edit List tab.
SpaceX_source_getOneLaunch
.
id
if it was not automatically selected.We will come back to the Record View Source soon. First, however, let's enhance our record list by adding a User Filter.
To add a User Filter:
New User Filter
link at the bottom of the grid.Launch Year
.launch_year
in the Rule Input dropdown.1
2
3
4
a!recordFilterChoices(
choiceLabels: {2015,2016,2017,2018,2019,2020},
choiceValues: {2015,2016,2017,2018,2019,2020}
)
Once again, at this point we can navigate to Tempo to verify that your new filter is interacting with your record list as expected. Incremental testing is a major key to success for expression-backed records.
For more practice, configure a second User Filter for the launch_success
parameter using the ri!launch_success rule input
Now that our search and filter is created, lets populate our record list with more meaningful data.
5
. This controls the batchSize that is passed into Paging Info input for your Record List Source.Mission Name
link. The grid configuration pane will show details about that column.2
.link
under Component to drill into the column.Record Link
under Links to drill futher into the column.rp!id
under Identifier to edit the record link identifier. Set it to flight_number
.You should end up with a list view that looks like this:
We're almost there! The last step of this tutorial is to create the summary view.
A summary view for an expression-backed record is no different, structurally, from a summary view for any other type of record. What may be different, however, is how you acquire the data.
SpaceX_LaunchSummaryView
and choose a rule folder to save into.
Copy the expression below and paste in the interface definition.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
a!boxLayout(
label: "Flight #"&ri!launch.flight_number&" Launch Info",
contents: {
a!sideBySideLayout(
items: {
a!sideBySideItem(
item: a!textField(
label: "Date",
labelPosition: "ADJACENT",
value: ri!launch.launch_date_utc,
readOnly: true
)
),
a!sideBySideItem(
item: a!textField(
label: "Name",
labelPosition: "ADJACENT",
value: ri!launch.rocket.rocket_name,
readOnly: true
)
),
a!sideBySideItem(
item: a!richTextDisplayField(
label: "Success",
labelPosition: "ADJACENT",
value: a!richTextIcon(
icon:
if(
ri!launch.launch_success,
"check-circle",
"times-circle"
),
color:
if(
ri!launch.launch_success,
"POSITIVE",
"NEGATIVE"
)
)
)
)
}
),
a!textField(
label: "Details",
labelPosition: "ADJACENT",
value: ri!launch.details,
showWhen: not(isnull(ri!launch.details)),
readOnly: true
)
},
iscollapsible: true
)
The last steps are to get the record's title and summary view configured.
rf!mission_name
to give each record the title of launch mission.In the Interface expression editor, call the interface in the expression editor and pass in rp!id
. Your expression should look something like the example below.
1
2
3
4
5
6
7
8
9
10
11
12
rule!SpaceX_LaunchSummaryView(
launch:
'type!{urn:com:appian:types:spacex}SpaceX_Launch'(
flight_number: rf!flight_number,
mission_name: rf!mission_name,
launch_year: rf!launch_year,
launch_date_utc: rf!launch_date_utc,
rocket: rf!rocket,
launch_success: rf!launch_success,
details: rf!details
)
)
Congratulations! Your record is complete. It should look something like:
If you want to use a record picker with your expression-backed record, it should just work as expected. You should not need to change your record definition. But, in case something is not working as desired, here are a few notes on how the record picker works and how you can tweak your record design to optimize for a record picker.
If this picker does not meet your needs in some way, use a custom picker.