The capabilities described on this page are included in Appian's standard capability tier. Usage limits may apply. |
For your portal users to complete their workflows and tasks with ease, they'll likely need to enter or view data within your portal. Writing and querying data in a portal allows you and your users to easily get and share information.
For example, users may need to view data, like:
Or they may need to add or update data through actions, such as:
While you're reaching a broader audience with more information, you still need your data to stay safe. The isolated architecture of Appian Portals gives you peace of mind while allowing portal users to query and write data from Appian.
The best way to work with data in portals is using record types. However, you can also write and query data using CDTs, as long as you create a custom integration using a web API and integration object. And, if you have a publicly-available external database, you can directly write to or query from the database. See Working with Data in Appian to help you understand when to use a record type and when to use a CDT.
This page walks through how to work with data using all of the methods available from a portal.
Record types makes it easy to work with your data in portals, without creating a custom integration. Use them to:
Tip: You can also use a!startProcess() in a portal interface to perform anything else possible with a process model, like creating a user or sending an email. You can use these same steps to create a portal that performs any other Appian process.
Records-powered interface components such as the record picker component and charts or grids that use a record type as a source aren't compatible with portals, so you'll need to design your interfaces without these features. These examples show you some ways to do that.
To build your pickers, grids, and charts, you must use an a!queryRecordType()
expression to query and display your record data in a portal. You cannot use a record type as the data source.
Let's look at some ways to configure your interface components using a!queryRecordType()
.
To display record data in a grid, you'll need to create a query using a!queryRecordType() and store the resulting values in a local variable. Then, you can use that local variable as the source of your grid. You can then add columns to the grid and even allow users to filter the grid using a dropdown component.
The following steps illustrate how to build an interface using a!queryRecordType()
to display record data in a grid, and then publish this interface in a portal.
This example uses data from the Appian Retail application, available for free in Appian Community Edition. To follow along with this example, go to Appian Community to request the latest Appian Community Edition site.
If you do not see the Appian Retail application available in your existing Appian Community Edition site, request a new Appian Community Edition site to get the latest application contents available.
This example uses data from the following record types in the Appian Retail application:
We'll start this example by creating an interface that display all product information, including its category and subcategory, in a grid. To display all this data, we'll query the Product record type and its related record types Product Category and Product Subcategory.
We also want portal users to be able to filter this grid. To do this, we'll create another query to return the list of categories and subcategories and then use those values in a dropdown component.
Let's get started.
By default, a!queryRecordType()
returns only the record fields from the record type specified in the recordType parameter. In order to return related record fields, you must specify the related records in the fields parameter.
To create an interface that queries record data:
Copy and paste the following expression:
Note: These record type references are specific to the Appian Retail application. If you're following along in the Appian Retail application, you can copy and paste this expression without updating the record type references.
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
a!localVariables(
/* Query to return each product category to use in a dropdown component */
local!categories: a!queryRecordType(
recordType: 'recordType!{05aad323-28b5-4341-8773-b615b44bc965}Product Category',
fields: {
'recordType!{05aad323-28b5-4341-8773-b615b44bc965}Product Category.fields.{963f051f-baea-4a23-8481-e365bf972a74}name',
'recordType!{05aad323-28b5-4341-8773-b615b44bc965}Product Category.fields.{ce16d9d2-57d9-44b6-b62f-1bb2a68bceae}productCategoryId'
},
pagingInfo: a!pagingInfo(startIndex: 1, batchSize: 50)
).data,
/* Query to return each product subcategory to use in a dropdown component */
local!subcategories: a!queryRecordType(
recordType: 'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory',
fields: {
'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{6f4fce8f-2487-4997-a365-985b895f90d8}name',
'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId'
},
pagingInfo: a!pagingInfo(startIndex: 1, batchSize: 50)
).data,
/* Local variables used to save the selected Category and Subcategory values from the dropdown components */
local!selectedCategory,
local!selectedSubcategory,
/* Query to return the record data to display in the grid */
local!data: a!queryRecordType(
recordType: 'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product',
fields: {
/* Returns all fields from the Category and Subcategory related record types */
/* Fields from related records are not automatically returned unless specified here */
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.relationships.{61e25c34-c4ba-4315-8da4-b2ed06d9b5ae}productCategory',
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory'
},
/* Query filters to filter data in the grid based on the selected category and subcategory from the dropdown components */
filters: {
a!queryFilter(
field: 'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.relationships.{61e25c34-c4ba-4315-8da4-b2ed06d9b5ae}productCategory.fields.{ce16d9d2-57d9-44b6-b62f-1bb2a68bceae}productCategoryId',
operator: if(
isnull(local!selectedCategory),
"not null",
"="
),
value: local!selectedCategory
),
a!queryFilter(
field: 'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId',
operator: if(
isnull(local!selectedSubcategory),
"not null",
"="
),
value: local!selectedSubcategory
)
},
pagingInfo: a!pagingInfo(startIndex: 1, batchSize: 500)
).data,
{
a!sectionLayout(
label: "Products",
contents: {
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!dropdownField(
label: "Category",
labelPosition: "ABOVE",
placeholder: "--- Select a Value ---",
choiceLabels: local!categories['recordType!{05aad323-28b5-4341-8773-b615b44bc965}Product Category.fields.{963f051f-baea-4a23-8481-e365bf972a74}name'],
choiceValues: local!categories['recordType!{05aad323-28b5-4341-8773-b615b44bc965}Product Category.fields.{ce16d9d2-57d9-44b6-b62f-1bb2a68bceae}productCategoryId'],
value: local!selectedCategory,
saveInto: local!selectedCategory,
searchDisplay: "AUTO",
validations: {}
)
}
),
a!columnLayout(
contents: {
a!dropdownField(
label: "Subcategory",
labelPosition: "ABOVE",
placeholder: "--- Select a Value ---",
choiceLabels: local!subcategories['recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{6f4fce8f-2487-4997-a365-985b895f90d8}name'],
choiceValues: local!subcategories['recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId'],
value: local!selectedSubcategory,
saveInto: local!selectedSubcategory,
searchDisplay: "AUTO",
validations: {}
)
}
),
a!columnLayout(contents: {})
}
),
a!gridField(
labelPosition: "COLLAPSED",
data: local!data,
columns: {
a!gridColumn(
label: "Name",
value: fv!row['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{afc5b005-2317-4c13-a132-62714c994bc9}name']
),
a!gridColumn(
label: "Product Number",
value: fv!row['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{06fac494-e9bc-4abe-a3a3-6cdfa6d8db49}productNumber']
),
a!gridColumn(
label: "Category",
value: fv!row['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.relationships.{61e25c34-c4ba-4315-8da4-b2ed06d9b5ae}productCategory.fields.{963f051f-baea-4a23-8481-e365bf972a74}name']
),
a!gridColumn(
label: "Subcategory",
value: fv!row['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.fields.{6f4fce8f-2487-4997-a365-985b895f90d8}name']
)
},
validations: {}
)
}
)
}
)
Before you can view the record data in a published portal, you need to give the portal the appropriate permissions to view the data. This is done using a service account, which acts on behalf of your portal users.
To provide the portal the appropriate permissions:
In this last step, we'll publish the portal so we can see the record data in our grid.
To publish and view the portal:
The best way to create, update, and delete data from a portal is to use a!startProcess() in the saveInto parameter of a portal interface. Then write that data directly to a record using the Write Records or Delete Records smart services.
You can use these instructions for more than just writing data. Simply call any process model using a!startProcess()
.
This simplified example uses a form to add a new product to the Product record type.
This example uses data from the Appian Retail application, available for free in Appian Community Edition. To follow along with this example, go to Appian Community to request the latest Appian Community Edition site.
If you do not see the Appian Retail application available in your existing Appian Community Edition site, request a new Appian Community Edition site to get the latest application contents available.
This example uses data from the following record types in the Appian Retail application:
In this step, we'll create a process model to add new products. We'll then create a constant to point to the process model so we can reference it in an interface later.
We'll just go through the basics of setting up a process model in this example. For more information on setting up process models with additional capabilities, see the Process Modeling Tutorial.
To create the process model:
Property | Value |
---|---|
Name | Enter productRecords . |
Type | Select Product (Record Type). |
Parameter | Select the Allow the value for the variable to be provided when starting a process checkbox. |
In the Data tab of the Write Records smart service node, on the Inputs tab, set the Value of Records to =pv!productRecords
and click OK.
AR_ADD_PRODUCT_PROCESS_MODEL
.In this step, we'll create an interface so portal users can add new products.
Since the record picker component is incompatible with portals, this example includes adding a dropdown list using a related record type to help illustrate how to do this manually. See the Query record data to display in a grid example for more information on how to query related record types in a portal.
In the onSuccess parameter of a!startProcess()
, we are using the ProcessInfo data type to save just the record values, instead of all of the process information.
To create an interface that starts a process:
Copy and paste the following expression:
Note: These record type references are specific to the Appian Retail application. If you're following along in the Appian Retail application, you can copy and paste this expression without updating the record type references.
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
a!localVariables(
local!result,
local!productRecordData: 'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product'(
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{afc5b005-2317-4c13-a132-62714c994bc9}name': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{06fac494-e9bc-4abe-a3a3-6cdfa6d8db49}productNumber': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{debc9655-4313-49e4-b906-8ba472326d97}safetyStockLevel': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6e28c019-12ab-4859-a8b3-8d52764e4944}reorderPoint': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{3779db6a-45a8-49b4-8dd9-23087c00dde7}standardCost': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6380baa1-23f9-409a-9b48-10e37d587fa3}listPrice': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{7f3e3cd2-6023-4190-9607-17d62448c592}daysToManufacture': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId': null
),
/* Query to return each product subcategory to use in the dropdown component */
local!subcategory: a!queryRecordType(
recordType: 'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory',
fields: {
'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{6f4fce8f-2487-4997-a365-985b895f90d8}name',
'recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId'
},
pagingInfo: a!pagingInfo(startIndex: 1, batchSize: 50)
).data,
local!submitted: false,
if(
local!submitted = false,
a!formLayout(
label: "Add a Product",
contents: {
a!cardLayout(
contents: {
a!columnsLayout(
columns: {
a!columnLayout(
contents: {
a!textField(
label: "Name",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{afc5b005-2317-4c13-a132-62714c994bc9}name'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{afc5b005-2317-4c13-a132-62714c994bc9}name'],
refreshAfter: "UNFOCUS",
required: true
),
a!textField(
label: "Product Number",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{06fac494-e9bc-4abe-a3a3-6cdfa6d8db49}productNumber'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{06fac494-e9bc-4abe-a3a3-6cdfa6d8db49}productNumber'],
refreshAfter: "UNFOCUS",
required: true
),
a!textField(
label: "Standard Cost",
labelPosition: "ABOVE",
value: a!currency(
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{3779db6a-45a8-49b4-8dd9-23087c00dde7}standardCost'],
isoCode: "USD"
),
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{3779db6a-45a8-49b4-8dd9-23087c00dde7}standardCost'],
refreshAfter: "UNFOCUS",
required: true
),
a!textField(
label: "List Price",
labelPosition: "ABOVE",
value: a!currency(
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6380baa1-23f9-409a-9b48-10e37d587fa3}listPrice'],
isoCode: "USD"
),
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6380baa1-23f9-409a-9b48-10e37d587fa3}listPrice'],
refreshAfter: "UNFOCUS",
required: true
),
/* Because the record picker component is incompatible with portals,
we manually set up a dropdown component using the local!subcategory query*/
a!dropdownField(
label: "Subcategory",
labelPosition: "ABOVE",
placeholder: "--- Select a Subcategory ---",
choiceLabels: local!subcategory['recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{6f4fce8f-2487-4997-a365-985b895f90d8}name'],
choiceValues: local!subcategory['recordType!{ef54f217-d426-4f2b-9955-3859c9919525}Product Subcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId'],
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{1deb35f9-8529-4f2f-b336-6774c614531d}productSubcategoryId'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{1deb35f9-8529-4f2f-b336-6774c614531d}productSubcategoryId'],
searchDisplay: "AUTO"
)
}
),
a!columnLayout(
contents: {
a!integerField(
label: "Safety Stock Level",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{debc9655-4313-49e4-b906-8ba472326d97}safetyStockLevel'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{debc9655-4313-49e4-b906-8ba472326d97}safetyStockLevel'],
refreshAfter: "UNFOCUS",
required: true
),
a!integerField(
label: "Reorder Point",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6e28c019-12ab-4859-a8b3-8d52764e4944}reorderPoint'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6e28c019-12ab-4859-a8b3-8d52764e4944}reorderPoint'],
refreshAfter: "UNFOCUS",
required: true
),
a!integerField(
label: "Days to Manufacture",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{7f3e3cd2-6023-4190-9607-17d62448c592}daysToManufacture'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{7f3e3cd2-6023-4190-9607-17d62448c592}daysToManufacture'],
refreshAfter: "UNFOCUS",
required: true
),
a!dateField(
label: "Sell Start Date",
labelPosition: "ABOVE",
value: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{726cc562-42a3-47b7-b8c7-ddcdc3e26540}sellStartDate'],
saveInto: local!productRecordData['recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{726cc562-42a3-47b7-b8c7-ddcdc3e26540}sellStartDate'],
required: true
)
}
)
}
)
},
height: "AUTO",
style: "TRANSPARENT",
marginBelow: "STANDARD"
)
},
buttons: a!buttonLayout(
primaryButtons: {
a!buttonWidget(
label: "Add Product",
saveInto: a!startProcess(
processModel: cons!AR_ADD_PRODUCT_PROCESS_MODEL,
processParameters: {
/* This is the name of the process variable from the process model*/
productRecords: local!productRecordData
},
onSuccess: {
/* Sets local!submitted to true so that a confirmation message can display, instead of the form*/
a!save(local!submitted, true),
a!save(
local!result,
/* Index into the productRecords process variable to save just the record values,
instead of all of the process information*/
fv!processInfo.pv.productRecords
),
a!save(
/* This resets the form values to null to reset the form*/
local!productRecordData,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product'(
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{afc5b005-2317-4c13-a132-62714c994bc9}name': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{06fac494-e9bc-4abe-a3a3-6cdfa6d8db49}productNumber': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{debc9655-4313-49e4-b906-8ba472326d97}safetyStockLevel': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6e28c019-12ab-4859-a8b3-8d52764e4944}reorderPoint': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{3779db6a-45a8-49b4-8dd9-23087c00dde7}standardCost': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{6380baa1-23f9-409a-9b48-10e37d587fa3}listPrice': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.fields.{7f3e3cd2-6023-4190-9607-17d62448c592}daysToManufacture': null,
'recordType!{dee327a7-3854-45e5-a952-d3d7d3edcc82}Product.relationships.{d3a62d4a-3268-48dc-9563-3b99c33715d1}productSubcategory.fields.{b0f94c2f-cba6-4565-ae39-fa81da69b1d5}productSubcategoryId': null
)
)
}
),
submit: true,
style: "PRIMARY",
loadingIndicator: true
)
},
secondaryButtons: {
a!buttonWidget(
label: "Cancel",
value: true,
saveInto: {},
submit: true,
style: "NORMAL",
validate: false
)
}
)
),
/* Success message that displays when local!submitted=true*/
{
a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: "Product added.",
size: "MEDIUM_PLUS",
style: "STRONG"
),
align: "CENTER"
),
a!richTextDisplayField(
labelPosition: "COLLAPSED",
value: a!richTextItem(
text: "Thank you for submitting a new product."
),
align: "CENTER"
),
/* This link reopens the form. Replace the link with a link to your portal */
a!linkField(
labelPosition: "COLLAPSED",
align: "CENTER",
links: {
a!safeLink(
label: "Add another product.",
uri: "REPLACE THIS WITH A LINK TO YOUR PORTAL",
openLinkIn: "SAME_TAB"
)
}
)
}
)
)
Before you can view the record data in a published portal, you need to give the portal the appropriate permissions to view the data. This is done using a service account, which acts on behalf of your portal users.
To provide the portal the appropriate permissions:
In order to make sure the process executes correctly in a portal, you need to publish the portal in order to test that the portal starts the process and writes the data correctly.
To test the process in a published portal:
For Status, choose All Process Instances and verify an instance is listed with a completed status.
If there are scenarios where you cannot use record types, you can query and write data using custom data types (CDTs) and data stores.
To work with CDTs in a portal, you can call an integration object in the portal interface to request to write to or query data from Appian. The integration talks directly to a web API in Appian, which completes the request and sends a response back to the integration in the portal.
The integration and web API form a bridge that connects your portal to Appian and allows data to pass through. The integration is on one side—in your portal—and the web API is on the other side—in Appian.
The diagram below shows how the portal and Appian work together to allow for the flow of data, without the use of records.
You can query data in your portal using a CDT by creating a custom integration using a web API, integration object, and connected system.
This section outlines how to query data in a portal using a CDT and data store.
You'll first need to create a web API that queries your data store. This is pretty simple since you can use an API template that automatically populates some common fields and selections to start you out.
To create a web API:
For general help creating a web API, check out Creating web APIs. For help creating a web API that queries data, go through the web API Tutorial, which walks you through creating a web API that queries a record type.
In order to connect to the web API, you'll need an API key that is associated to a service account. Administrators can create these in the Admin Console. When creating the API key, copy the API key and keep it in a secure place. You can't access it again.
In order to query data, the service account needs to have Viewer permissions to the web API and data store.
To give your service account the appropriate access:
You can use the same service account that you set up with your API key as the portal service account that you add in the portal object. Just be sure to use the same service account to grant the required permissions to other design objects used in your portal.
When deploying a portal object to a different environment, make sure that there is a service account of the same username in the target environment and make sure it is in the same groups.
Now you need to add your API key and credentials to a connected system to use with the integration.
To create a connected system for authentication:
Appian-API-Key
.Once you've created your API and authentication objects, create an integration object that is set up to query data using the connected system that you just created.
Make sure that you have the GET content type and Convert JSON to Appian value response body parsing selected. This casts the JSON query results returned by your web API back to a dictionary.
For general help creating an integration, follow the steps in Create an Integration.
In your portal interface, call your integration. Cast the dictionary results returned by your integration into the CDT that you're using for your data. This will let you easily reference your queried data throughout your portal without extra formatting.
Tip: Instead of casting your integration results from a dictionary to a CDT directly within your interface, you have the option to create individual or reusable expression rules to cast your integration results. You would then call the integration from your interface and wrap it in the expression rule.
Using local variables within an interface, you can cast your integration results from a dictionary to whichever CDT you're using to work with your data.
1
2
3
4
5
6
7
8
9
a!localVariables(
local!data: cast(
type!myCDT,
rule!myQueryIntegration().result.body
),
a!gridField(
data: local!data
)
)
For further examples, check out calling an integration to query data.
After creating a portal object, adding the interface, and publishing the portal, be sure to fully test the published portal.
When you deploy your portal object to a different environment, include an import customization file (ICF) in the app or deployment package. In the ICF, provide the API key from your target environment as well as any other connected system credentials and information. For more information, see Deploying a portal.
You can write data from your portal using a CDT by creating a custom integration using a web API, integration object, and connected system.
This section outlines how to write data from a portal using a CDT and data store.
You'll first need to create a web API that writes data directly to your data store. This is pretty simple since you can use an API template that automatically populates some common fields and selections to start you out.
To create a web API:
If you need help creating a web API that writes data, go through the Web API Tutorial Level II, which walks you through creating a web API that writes data to a record type.
In order to connect to the web API, you'll need an API key that is associated to a service account. Administrators can create these in the Admin Console. When creating the API key, copy the API key and keep it in a secure place. You can't access it again.
In order to write data, the service account needs to have Viewer permissions to the web API and data store.
To give your service account the appropriate access:
You can use the same service account that you set up with your API key as the portal service account that you add in the portal object. Just be sure to use the same service account to grant the required permissions to other design objects used in your portal, like records, processes, and document folders.
When deploying a portal object to a different environment, make sure that there is a service account of the same username in the target environment and make sure it is in the same groups.
Now you need to add your API key to a connected system for the integration.
To create a connected system for authentication:
Appian-API-Key
.Once you've created your API and authentication objects, create an integration that is set up to modify data using the connected system that you just created.
To configure your integration:
For more help, see Create an Integration.
Next, call your integration from your portal interface. For examples on how and where to call your integration in the portal, check out Calling an Integration to Modify Data.
After creating a portal object, adding the interface, and publishing the portal, be sure to fully test the published portal.
When you deploy your portal object to a different environment, include an import customization file (ICF) with the application or in a deployment package. In the ICF, provide the API key from the target environment as well as any other connected system credentials and information. For more information, see Deploying a portal.
The best way to work with data in portals is using record types. However, you also have the option of directly connecting to a publicly-available external database.
There are a couple of reasons why you might want to take this approach:
In order to connect a portal in this way, the database you're connecting to must meet the following criteria:
This section explains how to directly connect to an external database to query or write data in your portal.
To set up your external data source to query or write data:
To write and query data in the external database, you need to give the portal the appropriate permissions to the data store. This is done using a service account, which acts on behalf of your portal users.
In order to write and query data, the service account needs have Viewer permissions to the data store.
To give your service account the appropriate access:
You can only link one service account in the portal object. If you're using a service account to give access to records, processes, or documents, use the same service account for the data store.
In your portal interface, use a!writeToDataStoreEntity(), a!writeToMultipleDataStoreEntities(), a!deleteDataStoreEntity(), and/or a!queryEntity() just as you normally would in Appian.
If you are querying data, we recommend that you cast the dictionary returned from your query to a CDT.
Be aware that any calls to your external database go directly to the database and don't pass through Appian.
When you deploy your portal object to a different environment, include an import customization file (ICF) in the app or deployment package. In the ICF, provide your data source connected system credentials.
For more information on deploying portals, see Deploying a portal.
For more information on deploying data source connected systems, see Deploying data source connected systems.
Working with Data in a Portal