ArcGIS Enterprise Custom Data Feeds (CDF)
A Custom Data Feed is a Node.js provider — running on Koop.js inside the ArcGIS Enterprise SDK runtime — that pulls from any external source (REST API, database, file) and exposes it as a standard ArcGIS Feature Service. Your provider returns GeoJSON; the framework translates it into the GeoServices REST spec that ArcGIS clients consume.
Deployment and registration on ArcGIS Server (uploading
,
) is
out of scope here — this skill covers authoring and local testing of the provider.
Confirm the target Enterprise version first — before any code
CDF arrived in ArcGIS Enterprise 11.1 (it does not exist in 10.x), and its capabilities are gated by release. The provider you write depends entirely on the version it must run against, so pin it before writing anything:
- Ask which ArcGIS Enterprise version(s) must run this provider. A provider compiled against a newer runtime does not run on an older server — build against the lowest target release.
- 12.x requires a recompile. Providers must be recompiled in 12.x to run there; a provider built on 11.x is not binary-compatible with a 12.x runtime.
- Match the OS family and Node.js major version of the development machine to the target server, or the compiled provider fails to load.
For the current release, the per-version capability list, and any breaking changes, read the live
ArcGIS Enterprise SDK CDF guide and its "What's New" pages — do not rely on a restated matrix, which goes stale. To confirm a class, method, or option exists at a version, defer to the
skill.
The 12.0 generation boundary
CDF fundamentally changed at 12.0 — treat pre-12.0 and 12.0+ as two generations and never mix their assumptions:
- The npm module is retired in 12.0. Store credentials in a local JSON file inside and it directly. On 11.1–11.5, is standard (top-level keys must be unique across all providers in the app).
- 12.0 upgrade trap: a provider that used the module will not auto-re-register after an upgrade to 12.0. Migrate it to local JSON config before upgrading.
- (below) exists only from 12.0.
Project scaffolding — the cdf CLI
bash
cdf createapp my-cdf-app # once per project
cd my-cdf-app
cdf createprovider my-provider # a provider inside the app
npm start # local dev server (HTTP :8080)
cdf export my-provider # produce a deployable .cdpk
Generated shape (essentials):
providers/my-provider/src/index.js
(registration),
(core logic),
(provider config),
(provider deps).
Install npm packages at the provider level (
), never at the app level. Never modify
— the bundled Koop packages are read-only.
The Model class
exports a class with up to four methods. The "since" column is durable release history, not a live matrix:
| Method | Required | Since | Purpose |
|---|
| Yes | 11.1 | Fetch data; return a GeoJSON FeatureCollection |
| No | 11.4 | Handle applyEdits (adds / updates / deletes) |
| No | 11.5 | Pre-request authorization; throw to reject |
| No | 12.0 | Return + for edit reprojection |
returns a GeoJSON
with a
block. Use the
async/await form and
never mix async and callback patterns in the same method — doing so crashes the process. See
references/geojson-response-format.md for the full response schema and
references/provider-patterns.md for the full-fetch vs. pass-through patterns.
Key
properties:
(
,
, …),
(service parameter values),
,
/
req.query.resultRecordCount
(pagination),
req.query.returnCountOnly
(
→ return
),
(requires
forwardUserIdentity: true
on the service).
GeoJSON response rules
- All features in a layer must share the same geometry type.
- Always declare explicitly — never rely on type inference from the first feature; it produces wrong types.
- Always set to a unique numeric property. Omitting it forces full-feature hashing (slow, collision-prone). For editable providers, must reference a real field — auto-generated OIDs are unsupported.
- Default CRS is WGS84 (4326); set to the WKID when your data uses another.
- For multi-layer services, return
{ layers: [...], tables: [], metadata: { name, inputCrs } }
when is undefined.
- In the pass-through pattern, set (, , , , ) to for each filter you handled upstream so the framework does not double-apply it.
- sets the LRU cache TTL in seconds (cache capacity: 500 elements).
Guard destructive edits in editData()
writes
/
/
to the provider's
upstream store (a database, an external API) — these are irreversible data operations, so before generating any
code that updates or deletes, satisfy every point in order:
- Name the target. State the upstream system and the exact store/collection/table the writes hit. An unnamed target is a stop.
- Show what it is. Surface what the update/delete affects (which records, roughly how many) so the user sees what they are about to change or lose.
- Confirm it is not production. Say so explicitly and get the user's confirmation before proceeding.
- Prefer the reversible form first. Offer a read-first / count-first path so the blast radius is known before the destructive write runs.
- Never emit blind. Withhold the update/delete code until 1–4 are satisfied and the user confirms — even when the user sounds confident.
Return
{ addResults, updateResults, deleteResults }
with per-item
{ objectId, success, error? }
. Error codes:
insert,
delete,
update.
is
not enforced by the framework — implement transaction logic in
yourself. Gate writes with
(throw to reject).
Configuration and secrets
- 12.0+: a local JSON config file inside . Add it to .
- 11.1–11.5: the module ().
- Never hardcode credentials; never commit config JSON with real secrets.
Done when
The target Enterprise version is fixed (and the provider built against the lowest target, recompiled for 12.x if needed); pre-12.0 vs 12.0+ config handling matches that version; every
response declares
and a numeric
with one geometry type per layer; secrets live in gitignored config, never inline; any
update/delete has passed through the guard; and version-specific claims were confirmed against the live Enterprise SDK CDF guide via
.
Reference files
| File | When to read |
|---|
| references/geojson-response-format.md | Full metadata schema, field types, multi-layer format, edit templates |
| references/provider-patterns.md | Full-fetch vs. pass-through, count/extent shortcuts, upstream auth |
| references/examples/minimal-provider.md | Complete working read-only provider (illustrative, targeting 12.0) |
| references/examples/editable-provider.md | Complete editable provider with a database backend (illustrative, targeting 12.0) |