apex Step Type Properties
To set up an apex step, create an Apex class that returns data in a shape that Analytics can consume. And then define the step with the apex step type in the dashboard JSON. The step calls the Apex REST endpoint to return the data from the Apex controller class.
Like a soql step, the Apex controller can return tabular data. Unlike saql and soql step types, the apex step type doesn’t define the "strings", "numbers", and "groups" arrays. The Apex class response must declare these column types.
When you define an apex step, you can use a selection or results binding on the body parameter in the step JSON. You can also reference this step type in a results binding. This step type doesn’t support faceting. If you run a Analytics REST API query using an apex step, the query runs as the logged-in user. Each REST API query counts against the org’s API limits.
Note the following limitations with apex steps:
- If you include dashboards in a package, apex steps aren’t included. You must migrate the Apex classes separately.
- The Android mobile app doesn’t support this type of step.
| Field Name | Description |
|---|---|
| type | Step type. Set to apex. |
| label | Step label, which is primarily used for display in the dashboard designer user interface. |
| query | Query that returns the results. Can consist of the following parameters:
|
apex Step
1"GetStockData": {
2 "query": {
3 "body": {
4 "symbol": "CRM"
5 },
6 "path": "stocks"
7 },
8 "type": "apex"
9}Example
To create this dashboard, complete the following steps:
- Create the Apex controller class that gets the data from the stock website.
- Add the stock website to the allowed sites in Salesforce.
- Create the apex step in addition to the other dashboard widgets.
Create the Apex Class
- From setup, enter Apex Classes in the Quick Find box, and select Apex Classes.
-
Click New.

-
Add the following code.
1@RestResource(urlMapping='/stocks') 2global with sharing class StocksRestController { 3 4 @HttpPost 5 global static String stocks() { 6 ApexStepRequest stepRequest = new ApexStepRequest(new ApexStepRequest.Parameter[]{ 7 new ApexStepRequest.Parameter('symbol', ApexStepRequest.ParameterType.STRING_PARAM) 8 }); 9 10 // fetch some stock data 11 HttpRequest request = new HttpRequest(); 12 Http http = new Http(); 13 // make sure this domain is whitelisted in the proxy 14 request.setEndpoint('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&interval=5min&apikey=1QRP8ATD7134OVMA&symbol=' 15 // default to CRM 16 + stepRequest.getStringParam('symbol', 'CRM') 17 ); 18 request.setMethod('GET'); 19 20 try { 21 HTTPResponse response = http.send(request); 22 JSONParser parser = JSON.createParser(response.getBody()); 23 24 List<Map<String, Object>> returnItems = new List<Map<String, Object>>(); 25 while (parser.nextToken() != null) { 26 if (parser.getCurrentToken() == JSONToken.START_OBJECT && parser.getCurrentName() != null && parser.getCurrentName().startsWith('20')) { 27 String dateLabel = parser.getCurrentName(); 28 parser.nextToken(); 29 parser.nextToken(); 30 System.debug(parser.getText()); 31 32 Map<String, Object> curRow = new Map<String, Object>(); 33 curRow.put('date', dateLabel); 34 curRow.put('value', Double.valueOf(parser.getText())); 35 returnItems.add(curRow); 36 } 37 } 38 39 return JSON.serialize(new ApexStepResponse(returnItems)); 40 } catch(Exception exp) { 41 System.debug('exception '+exp); 42 } 43 44 45 return ''; 46 } 47 48} -
Add two more Apex classes for ApexStepRequest and ApexStepResponse to support the Apex stocks class. The code for these classes, along with the stock step code, can be found in this public AnalyticsApexSteps GitHub repo, in the /force-app/main/apex/common directory. This GitHub repo also contains other Apex step examples.
-
Click Save.
Allow the External Website
- From setup, enter Remote in the Quick Find box, and select Remote Site Settings.
-
Click New Remote Site.
-
Enter the remote site name and URL.
- Click Save.
Create the Apex Step
1"StockData": {
2 "query": {
3 "body": {
4 "symbol": "{{cell(CompaniesList_1.selection, 0, \"value\").asString()}}"
5 },
6 "path": "stocks"
7 },
8 "type": "apex"
9}- body
- The binding retrieves the stock symbol when a company is selected in the
toggle widget.
- path
- Specifies the REST API resource path to the Apex controller, as specified in the urlMapping of the RestResource annotation on the Apex controller.
The dashboard shows four widgets. The toggle widget is based on a static step that maps predefined company names to stock symbols. The table widget and line chart use the apex step to retrieve the results. The text widget uses a binding to concatenate “Selected symbol:” and the stock symbol of the selected company in the toggle widget.
Here’s the final dashboard JSON.1{
2 "label": "Stock Quotes",
3 "mobileDisabled": false,
4 "state": {
5 "dataSourceLinks": [],
6 "filters": [],
7 "gridLayouts": [
8 {
9 "name": "Default",
10 "numColumns": 12,
11 "pages": [
12 {
13 "label": "Untitled",
14 "name": "b177a6e6-8ab6-4914-af36-09c7fa23e0c8",
15 "widgets": [
16 {
17 "colspan": 6,
18 "column": 0,
19 "name": "pillbox_1",
20 "row": 0,
21 "rowspan": 1,
22 "widgetStyle": {
23 "borderEdges": []
24 }
25 },
26 {
27 "colspan": 5,
28 "column": 7,
29 "name": "text_1",
30 "row": 0,
31 "rowspan": 1,
32 "widgetStyle": {
33 "borderEdges": []
34 }
35 },
36 {
37 "colspan": 6,
38 "column": 0,
39 "name": "table_1",
40 "row": 1,
41 "rowspan": 7,
42 "widgetStyle": {
43 "borderEdges": []
44 }
45 },
46 {
47 "colspan": 6,
48 "column": 6,
49 "name": "chart_1",
50 "row": 1,
51 "rowspan": 7,
52 "widgetStyle": {
53 "borderEdges": []
54 }
55 }
56 ]
57 }
58 ],
59 "rowHeight": "normal",
60 "selectors": [],
61 "style": {
62 "alignmentX": "left",
63 "alignmentY": "top",
64 "backgroundColor": "#F2F6FA",
65 "cellSpacingX": 8,
66 "cellSpacingY": 8,
67 "fit": "original",
68 "gutterColor": "#C5D3E0"
69 },
70 "version": 1
71 }
72 ],
73 "layouts": [],
74 "steps": {
75 "CompaniesList_1": {
76 "datasets": [],
77 "dimensions": [],
78 "groups": [],
79 "label": "CompaniesList",
80 "numbers": [],
81 "selectMode": "singlerequired",
82 "strings": [],
83 "type": "staticflex",
84 "values": [
85 {
86 "display": "Salesforce",
87 "value": "CRM"
88 },
89 {
90 "display": "Apple",
91 "value": "AAPL"
92 },
93 {
94 "display": "Oracle",
95 "value": "ORCL"
96 },
97 {
98 "display": "Twitter",
99 "value": "TWTR"
100 },
101 {
102 "display": "Facebook",
103 "value": "FB"
104 }
105 ],
106 "broadcastFacet": true
107 },
108 "StockData": {
109 "query": {
110 "body": {
111 "symbol": "{{cell(CompaniesList_1.selection, 0, \"value\").asString()}}"
112 },
113 "path": "stocks"
114 },
115 "type": "apex"
116 }
117 },
118 "widgetStyle": {
119 "backgroundColor": "#FFFFFF",
120 "borderColor": "#E6ECF2",
121 "borderEdges": [],
122 "borderRadius": 0,
123 "borderWidth": 1
124 },
125 "widgets": {
126 "table_1": {
127 "parameters": {
128 "borderColor": "#e0e5ee",
129 "borderWidth": 1,
130 "cell": {
131 "backgroundColor": "#ffffff",
132 "fontColor": "#16325c",
133 "fontSize": 12
134 },
135 "columns": [],
136 "customBulkActions": [],
137 "exploreLink": false,
138 "header": {
139 "backgroundColor": "#f4f6f9",
140 "fontColor": "#16325c",
141 "fontSize": 12
142 },
143 "innerMajorBorderColor": "#a8b7c7",
144 "innerMinorBorderColor": "#e0e5ee",
145 "mode": "fittocontainer",
146 "numberOfLines": 1,
147 "step": "StockData",
148 "verticalPadding": 8,
149 "evenRowColor": null,
150 "oddRowColor": null
151 },
152 "type": "table"
153 },
154 "pillbox_1": {
155 "parameters": {
156 "compact": false,
157 "exploreLink": false,
158 "step": "CompaniesList_1"
159 },
160 "type": "pillbox"
161 },
162 "text_1": {
163 "parameters": {
164 "fontSize": 20,
165 "text": "Selected symbol: {{ cell(CompaniesList_1.selection, 0, \"value\").asString() }}",
166 "textAlignment": "center",
167 "textColor": "#091A3E"
168 },
169 "type": "text"
170 },
171 "chart_1": {
172 "parameters": {
173 "autoFitMode": "keepLabels",
174 "showPoints": false,
175 "legend": {
176 "showHeader": true,
177 "show": true,
178 "customSize": "auto",
179 "position": "right-top",
180 "inside": false
181 },
182 "axisMode": "multi",
183 "tooltip": {
184 "showBinLabel": true,
185 "measures": "",
186 "showPercentage": false,
187 "showDimensions": true,
188 "showMeasures": true,
189 "customizeTooltip": false,
190 "dimensions": ""
191 },
192 "visualizationType": "line",
193 "dashLine": {
194 "measures": "",
195 "showDashLine": false
196 },
197 "exploreLink": false,
198 "title": {
199 "fontSize": 14,
200 "subtitleFontSize": 11,
201 "label": "",
202 "align": "center",
203 "subtitleLabel": ""
204 },
205 "trellis": {
206 "flipLabels": false,
207 "showGridLines": true,
208 "size": [
209 100,
210 100
211 ],
212 "enable": false,
213 "type": "x",
214 "chartsPerLine": 4
215 },
216 "fillArea": true,
217 "showZero": true,
218 "measureAxis2": {
219 "sqrtScale": false,
220 "showTitle": true,
221 "showAxis": true,
222 "title": "",
223 "customDomain": {
224 "showDomain": false
225 }
226 },
227 "measureAxis1": {
228 "sqrtScale": false,
229 "showTitle": true,
230 "showAxis": true,
231 "title": "",
232 "customDomain": {
233 "showDomain": false
234 }
235 },
236 "theme": "wave",
237 "step": "StockData",
238 "dimensionAxis": {
239 "showTitle": true,
240 "customSize": "auto",
241 "showAxis": true,
242 "title": "",
243 "icons": {
244 "useIcons": false,
245 "iconProps": {
246 "fit": "cover",
247 "column": "",
248 "type": "round"
249 }
250 }
251 },
252 "drawArea": {
253 "measure": "",
254 "showDrawArea": false,
255 "bounding1": "",
256 "bounding2": ""
257 }
258 },
259 "type": "chart"
260 }
261 }
262 },
263 "datasets": []
264}