Skip to content

This topic discusses the custom data provider specification.

Registration Object

Every custom data provider has a file named index.js that exports a registration object containing provider information. The object describes how to load and use the provider. The following is the code that is automatically included in the index.js file by the Custom Data Feeds (CDF) command line tool.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
const packageInfo = require('../package.json');
const csconfigInfo = require('../cdconfig.json');
const provider = {
  type: csconfigInfo.type,
  name: csconfigInfo.name,
  version: packageInfo.version,
  Model: require('./model')
};
module.exports = provider;

The table below describes each field in detail:

NameRequiredTypeDescription
typestringSet to 'provider'
namestringURL-safe provider name
versionstringProvider version number
Modelclassrequire statement for Model class
routesObject[]Array of provider-specific route objects
ControllerclassController class that supports route handling

The auto-generated code pulls the provider version from the package.json file and other fields from the cdconfig.json file. To modify these fields, change the corresponding values in the cdconfig.json file instead of the index.js file. In general, developers should have no need to edit this file. The explanation here is only for reference.

Model Class

Every custom data provider implements a Model class, which is usually in the model.js file. The Model class includes a required getData() method that will contain code for fetching and tranforming remote data into GeoJSON. It may optionally include the editData() method for handling feature editing, the authorize() method for handling custom user authorization, or the getMetadata() method. The following table describes the arguments that the getData() method accepts. The callback is optional because the getData() method may be an async function if desired.

NameDescription
req

Type: object
Description: Express.js request object.

Example
Use dark colors for code blocksCopy
1
2
3
4
5
6
async getData(req) {
  const layerId = req.params.layer;
  const whereClause = req.query.where;
  // rest of code
  return geojson;
}
callback

Type: function
Description: Optional function that either returns a GeoJSON object or an error. Using async syntax is recommended.

Example
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
getData(req, callback) {
  try{
    // rest of code
    callback(null, geojson)
  } catch (err) {
    callback(err)
  }
}

Request Object

The following table shows the properties that are availble on the request object. The example column is not exhaustive.

NameDescription
params

The params object will include service parameters and layer index.

Example
Use dark colors for code blocksCopy
1
2
const layerId = req.params.layer;
const sheetID = req.params.sheet_id;
query

The query object contains parameters sent by the requesting client. These are crucial for building efficient pass‑through providers. Refer to the ArcGIS REST documentation for full details.

Example
Use dark colors for code blocksCopy
1
2
3
const whereClause = req.query.where;
const resultOffset = req.query.resultOffset;
const resultRecordCount = req.query.resultRecordCount;
_user

The _user object is available when the service configuration option forwardUserIdentity is true.
Refer to the topic on creating custom data providers for more information.

Example
Use dark colors for code blocksCopy
1
2
const username = req._user.username;
const serverRoles = req._user.serverRoles;

GeoJSON Metadata Fields

Custom Data Feeds requires that the GeoJSON be decorated with a property called metadata. It should have an object as its value. This object contains information important for translating the GeoJSON to its feature service equivalent. The table below describes some of the key metadata properties:

FieldDetails
defaultVisibility

Type: Boolean
Description: Specifies the default visibility of the layer.
Default: true

description

Type: string
Description: Description of the layer.
Example: My amazing dataset

displayField

Type: string
Description: The name of the layer's primary display field.
Example: FLD0_SUBT_FC2

fields

Type: object[]
Description: An array of objects that define feature attributes. If you define one attribute, you must define all. If not set, inferred from the first feature. Recommended to always set this field to avoid incorrect inference.

Example
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
  {
    "name": "fireId",
    "type": "number",
    "alias": "fire ID",
    "editable": true
  },
  {
    "name": "fireName",
    "type": "string",
    "alias": "fire name",
    "editable": true
  }

Refer to this table for the possible type property values and their corresponding Esri Field Type.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  | Type          | Esri Field Type               |
  |---------------|-------------------------------|
  | double        | ESRI_FIELD_TYPE_DOUBLE        |
  | number        | ESRI_FIELD_TYPE_DOUBLE        |
  | integer       | ESRI_FIELD_TYPE_INTEGER       |
  | date          | ESRI_FIELD_TYPE_DATE          |
  | blob          | ESRI_FIELD_TYPE_BLOB          |
  | geometry      | ESRI_FIELD_TYPE_GEOMETRY      |
  | globalid      | ESRI_FIELD_TYPE_GLOBALID      |
  | guid          | ESRI_FIELD_TYPE_GUID          |
  | raster        | ESRI_FIELD_TYPE_RASTER        |
  | single        | ESRI_FIELD_TYPE_SINGLE        |
  | smallinteger  | ESRI_FIELD_TYPE_SMALL_INTEGER |
  | biginteger    | ESRI_FIELD_TYPE_BIG_INTEGER   |
  | bigint        | ESRI_FIELD_TYPE_BIG_INTEGER   |
  | xml           | ESRI_FIELD_TYPE_XML           |
  | string        | ESRI_FIELD_TYPE_STRING        |

See the ArcGIS REST Documentation for other details on data types such as coded value and range domains.

geometryType

Type: string
Description: Geometry type of features. If null, assumes tabular data. Possible values: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, null. If not set, inferred from the first feature. Recommended to always set this field.
Example: Point

hasZ

Type: Boolean
Description: Indicates whether the features have elevation (z) values.
Default: false
Example: false

id

Type: string
Description: The unique ID assigned to the layer.
Default: 0
Example: 1

idField

Type: string
Description: Field that uniquely identifies each feature. Used as the feature's OBJECTID. A 64-bit integer is safest for ArcGIS compatibility, though some clients support other types.
Example: rowID
Note on Auto-Generated ObjectIds: If this metadata property is not set, the custom data feeds framework will create a 64-bit integer objectId for the feature based on a numeric hash of the entire object that represents the feature. However, you do have the option of creating a numeric hash from a single attribute value rather than the entire feature. You may select a string-type attribute in your dataset that is unique as the idField, in which case the custom data feeds framework will create a numeric hashed objectId for only that field. This can improve performance over hashing the entire feature JSON.

inputCrs

Type: number
Description: WKID value for features returned in getData(). Defaults to 4326 (WGS84) unless specified. Ensures accurate reprojection. Setting this value allows you to override the expectation that the feature geometry return in the getData() method is some crs other than WGS84. Other supported aliases for inputCrs include: dataCrs, sourceSR, and crs.
Example: 3857

labelingInfo

Type: object[]
Description: Array of objects specifying rules for formatting feature labels.

Example
Use dark colors for code blocksCopy
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
{
  "symbol": {
    "kerning": true,
    "color": [214,75,75,255],
    "yoffset": 0,
    "xoffset": 0,
    "haloColor": [211,211,211,255],
    "rotated": false,
    "type": "esriTS",
    "horizontalAlignment": "center",
    "haloSize": 1,
    "angle": 0,
    "text": "",
    "verticalAlignment": "baseline",
    "font": {
      "size": 9.75,
      "family": "Arial"
    }
  },
  "maxScale": 577790,
  "repeatLabel": true,
  "where": "name = 'Costa Rica'",
  "minScale": 0,
  "labelExpressionInfo": {
    "expression": "$feature[\"name\"]"
  },
  "labelExpression": "[name]",
  "labelPlacement": "esriServerPolygonPlacementAlwaysHorizontal"
}

See the ArcGIS REST Documentation for more details on labeling options.

maxRecordCount

Type: number
Description: Maximum number of features the provider can return at once. Should match the remote data source's limit.
Default: 2000
Example: 2000

maxScale

Type: number
Description: Maximum scale (most zoomed in) at which the layer is visible.
Default: 0
Example: 99000

minScale

Type: number
Description: Minimum scale (most zoomed out) at which the layer is visible.
Default: 0
Example: 1000

name

Type: string
Description: The layer name.
Example: Test Layer

supportsPagination

Type: Boolean
Description: Indicates whether the query response supports pagination.
Default: true

templates

Type: object[]
Description: Defines attributes in the editing template and their default values. If not provided, a default template is created.

Example
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
  {
    "name": "Fires",
    "description": "Template for wildfire features",
    "drawingTool": "esriFeatureEditToolPoint",
    "prototype": {
      "attributes": {
        "fire_type": null,
        "fire_name": null,
        "acres": null
      }
    }
  }
]

If your data does not contain a field that can be used as the OBJECTID, you can leave idField undefined. In such cases Custom Data Feeds will create an OBJECTID for each feature by default. However, this is less than ideal as it impacts performance and in very rare cases may lead to OBJECTID collisions. The best practice is to have a field on the raw data that meets the requirements of idField. See Setting the idField Property for more details.

More GeoJSON Properties

The properties listed in the table below are not part of the metadata object. They are properties to be attached to the returned geoJSON object.

Example
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
async getData(req) {
  // rest of code
  return
  {
    ...geojson, // using spread operator to append filtersApplied and metadata to geojson
    filterApplied,
    metadata
  }
}
FieldDetails
filtersApplied

Type: object
Description: Indicates whether your code has already filtered the feature data. If true, the framework skips filtering. If false or omitted, the framework filters the data returned in getData() based on the request's query parameters. filtersApplied is important to use when employing a pass-through data loading pattern.

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
{
  where: true,
  objectIds: true,
  geometry: true,
  resultRecordCount: true,
  resultOffset: true
}

Routes and Controllers

Routes

To use provider-specific routes, ensure that the routes field in the index.js file points to a file that exports an array of route definition objects. The following table describes the fields that each route definition object must contain.

NameDescription
path

Type: string
Description: Express.js‑style route that includes optional parameters.

methods

Type: string[]
Description: HTTP methods that this route can handle.

handler

Type: string
Description: Name of the controller function that should handle requests at this route.

Controllers

Set the handler field of each route definition object to the function name that handles requests for that route. You can define these functions in a Controller class. The Controller field in the index.js file must reference the Controller class. Below is an example of how to implement a Controller class.

Use dark colors for code blocksCopy
1
2
3
4
5
6
function Controller (model) {
  this.model = model;
}
Controller.prototype.test = function (req, res) {
  res.status(200).json({version: '1.0.0'});
};

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.