JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 41
  • Score
    100M100P100Q100186F
  • License MIT

CLI tool for managing Dataverse Custom APIs - export, edit, and sync Custom API definitions between local JSON files and Dataverse environments

Package Exports

  • @brunsforge/dataverse-custom-api
  • @brunsforge/dataverse-custom-api/cli

Readme

Dataverse Custom API CLI

A Node.js/TypeScript CLI for managing Dataverse Custom APIs. It enables reading, exporting, editing, validating, diffing, planning, and synchronizing Custom API definitions between local JSON files and Dataverse environments.

The GitHub repository contains the TypeScript src/ source code. The compiled dist/ output is generated during build and included together with src/ in the published npm package.

Installation

npm install -g @brunsforge/dataverse-custom-api

Or from source:

git clone https://github.com/brunsforge/dataverse-custom-api.git
cd dataverse-custom-api
npm install
npm run build
npm link

Features

  • Connect to Dataverse environments: multiple authentication methods (Device Code, Client Secret, Interactive Browser)
  • Environment management: save, list, switch, and remove environments
  • Custom API management:
    • list available Custom APIs
    • export Custom APIs as local JSON artifacts
    • edit Custom API definitions locally
    • validate local definitions before syncing — catches type errors, missing fields, and invalid combinations with structured diagnostics
    • compare local definitions with Dataverse metadata
    • create sync plans (validation runs automatically; plan is blocked if errors are found)
    • execute single sync operations
    • execute full sync plans
    • create, update, or delete Custom API metadata in Dataverse
  • Simulation mode: preview operations with --simulate before applying changes
  • JSON and human-readable output: every command supports --json
  • Structured diagnostics: api validate --json returns a CcdvCommandResult envelope for VS Code extension integration

Warning: Commands that execute without --simulate may create, update, or delete Custom API metadata in the connected Dataverse environment.

Authentication and connection

Authentication methods

The CLI supports three authentication methods:

  1. Device Code Flow (recommended for interactive use)
  2. Client Secret (for automation / app-only access)
  3. Interactive Browser (for browser-based login)

Configuration

Create an auth.json file in the repository root based on one of the sample files:

  • auth.devicecode.example.json → Device Code auth
  • auth.clientsecret.example.json → Client Secret auth
  • auth.interactivebrowser.example.json → Interactive Browser auth

Device Code auth

{
  "tenantId": "your-tenant-id",
  "clientId": "your-client-id",
  "authMode": "deviceCode"
}

App registration setup:

  • Create an Azure AD / Microsoft Entra ID App Registration
  • Add API permissions: Dynamics CRM > user_impersonation
  • Add a redirect URI: https://login.microsoftonline.com/common/oauth2/nativeclient
  • No client secret is required

Client Secret auth

{
  "tenantId": "your-tenant-id",
  "clientId": "your-app-only-client-id",
  "clientSecret": "your-client-secret",
  "authMode": "clientSecret"
}

App registration setup:

  • Create an Azure AD / Microsoft Entra ID App Registration
  • Create a client secret
  • Add API permissions: Dynamics CRM > user_impersonation (or Dataverse application permissions for app-only access)
  • No redirect URI is required

Interactive Browser auth

{
  "tenantId": "your-tenant-id",
  "clientId": "your-client-id",
  "authMode": "interactiveBrowser"
}

Connect to an environment

dvc connect -u "https://your-org.crm.dynamics.com"

Output:

Connected and cached: https://your-org.crm.dynamics.com
Auth mode: deviceCode
Cache file: /path/to/cache/environment.json

Environment management

List environments

dvc env list

Output:

* env-001 (Production) -> https://prod.crm.dynamics.com
- env-002 (Development) -> https://dev.crm.dynamics.com

Show current environment

dvc env current

Output:

Active environment: env-001
Display name: Production
URL: https://prod.crm.dynamics.com
Auth mode: deviceCode

Switch environment

dvc env use -i env-002

Remove environment

dvc env remove -i env-001

Custom API management

List Custom APIs

dvc api list

Output:

* ccsm_MyCustomApi
- ccsm_AnotherApi
- sample_CustomFunction

Show active Custom API

dvc api current

Output:

Active API: ccsm_MyCustomApi
Cache file: /path/to/cache/active-api.json

Set active Custom API

dvc api use -n ccsm_MyCustomApi

Export Custom API

Exports the current Dataverse definition to a local JSON catalog file.

dvc api export
# or with explicit name:
dvc api export -n ccsm_MyCustomApi

Output:

Exported: /path/to/output/ccsm_MyCustomApi.json

Validate local definition

Validates the local JSON file against Dataverse field rules. Returns structured diagnostics with error codes, field paths, and suggested fixes. No network connection is required.

dvc api validate
# or with explicit name:
dvc api validate -n ccsm_MyCustomApi

Output — no issues:

Validation for: ccsm_MyCustomApi
Status: succeeded
No issues found.

Output — with diagnostics:

Validation for: ccsm_MyCustomApi
Status: validationFailed

ERROR [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] [ccsm_MyCustomApi > NewParameter].logicalEntityName
      Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName. Only Entity and EntityReference are allowed.
      Fix: Remove logicalEntityName or change the parameter type to Entity or EntityReference.

WARN  [CCDV_PARAM_NAME_RECOMMENDED_FORMAT] [ccsm_MyCustomApi > InputParam].name
      Request parameter 'InputParam' name 'InputParam' does not follow the recommended format 'ccsm_MyCustomApi.InputParam'.
      Fix: Set name to 'ccsm_MyCustomApi.InputParam'.

Summary: 1 error(s), 1 warning(s), 0 info(s)

JSON output returns a full CcdvCommandResult envelope:

dvc api validate --json
{
  "schemaVersion": "1.0.0",
  "status": "validationFailed",
  "command": "api validate",
  "startedAtUtc": "2025-01-15T10:00:00.000Z",
  "finishedAtUtc": "2025-01-15T10:00:00.001Z",
  "durationMs": 1,
  "diagnostics": [
    {
      "id": "diag-0001",
      "code": "CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED",
      "severity": "error",
      "category": "validation",
      "message": "Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.",
      "entityKind": "requestParameter",
      "parentUniqueName": "ccsm_MyCustomApi",
      "uniqueName": "NewParameter",
      "field": "logicalEntityName",
      "jsonPath": "$.requestParameters[?(@.uniqueName=='NewParameter')].logicalEntityName",
      "blocking": true,
      "suggestedFix": {
        "kind": "removeField",
        "field": "logicalEntityName",
        "message": "Remove logicalEntityName or change the parameter type to Entity or EntityReference."
      }
    }
  ]
}

Diagnostic severity levels:

Severity Meaning
error Blocking — Dataverse will reject the payload or the value is structurally invalid. api plan will not proceed.
warning Non-blocking — the definition is accepted but deviates from recommended practice (e.g. name format).
info Informational — e.g. an open Entity type without logicalEntityName, which is valid but may be unintentional.

Validated rules include:

  • uniqueName required, max 128 characters, must contain publisher prefix (e.g. ccsm_), only letters/digits/underscores
  • name and displayName required, max 100 characters
  • description max 300 characters
  • bindingType must be Global, Entity, or EntityCollection
  • boundEntityLogicalName required when bindingType is Entity or EntityCollection; must be absent for Global
  • allowedCustomProcessingStepType must be None, AsyncOnly, or SyncAndAsync
  • Functions (isFunction: true) must have at least one response property
  • Functions must not use open Entity/EntityCollection request parameters (without logicalEntityName)
  • logicalEntityName only allowed on parameters/properties with type Entity or EntityReference; automatically flagged for all other types
  • name format recommendation: {customApiUniqueName}.{childUniqueName}

Note: api plan runs the same validation internally. If blocking errors are found, the plan is not created and the error message references api validate for the full report. Always run api validate first to see all diagnostics before planning.

Compare local definition with Dataverse

Compares the local JSON file against the current Dataverse metadata and shows field-level differences.

dvc api diff
# or with explicit name:
dvc api diff -n ccsm_MyCustomApi

Output:

Diff for: ccsm_MyCustomApi
Differences found: yes
Custom API: update

Request parameter summary:
  none=1, create=1, update=0, delete=0, recreate=0
- create: NewParameter

Response property summary:
  none=2, create=0, update=0, delete=0, recreate=0

Use --json for the full machine-readable diff output.

Create sync plan

Compares local vs. Dataverse, generates an ordered operation list, and writes plan and state files. Validation runs automatically — if blocking errors exist, the plan is not created.

dvc api plan
# or with explicit name:
dvc api plan -n ccsm_MyCustomApi

Output:

Plan created: /path/to/output/ccsm_MyCustomApi.syncplan.json
State file: /path/to/output/ccsm_MyCustomApi.syncstate.json
Operations: 2
Destructive changes required: no
- [10] createRequestParameter NewParameter (new)
- [20] updateCustomApi ccsm_MyCustomApi (changed)

If validation fails:

Error: Sync plan blocked by 1 validation error(s). Run 'api validate' for the full report.
First error: [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.

Execute a single operation

# dry run:
dvc api exec-op -o "op-0010-createRequestParameter-NewParameter-<uuid>" --simulate

# live:
dvc api exec-op -o "op-0010-createRequestParameter-NewParameter-<uuid>"

Output:

Operation: op-0010-createRequestParameter-NewParameter-<uuid>
Status: succeeded
Message: createRequestParameter for NewParameter completed successfully.
Simulated: no
State file: /path/to/output/ccsm_MyCustomApi.syncstate.json

Warning: Without --simulate, the operation is executed against the connected Dataverse environment.

Execute full plan

# dry run:
dvc api exec-plan --simulate

# live:
dvc api exec-plan

Output:

Plan executed for: ccsm_MyCustomApi
Status: succeeded
State file: /path/to/output/ccsm_MyCustomApi.syncstate.json
- [10] createRequestParameter NewParameter: succeeded
- [20] updateCustomApi ccsm_MyCustomApi: succeeded

Warning: Without --simulate, all operations in the sync plan are executed in sequence against the connected Dataverse environment.

Check metadata consistency

Compares the local definition with current Dataverse metadata and reports field-level mismatches. Requires a network connection.

dvc api check-metadata

Output:

Metadata check for: ccsm_MyCustomApi
Status: ok
No metadata mismatches found.

Remove local artifacts

Removes local cached and exported files. Does not delete the Custom API from Dataverse.

dvc api remove
# or with explicit name:
dvc api remove -n ccsm_MyCustomApi

Typical workflows

1. Setup and connect

# Create auth configuration
cp auth.devicecode.example.json auth.json
# Edit auth.json with your tenant ID and client ID

# Connect to an environment
dvc connect -u "https://your-org.crm.dynamics.com"

2. Edit an existing Custom API

# List available APIs
dvc api list

# Set the API as active
dvc api use -n ccsm_ExistingApi

# Export the current Dataverse definition
dvc api export

# Edit the local JSON file
# e.g. add a request parameter, change a description

# Validate the local definition — fix any errors before proceeding
dvc api validate

# Review what changed vs. Dataverse
dvc api diff

# Create a sync plan (also validates internally)
dvc api plan

# Dry run first
dvc api exec-plan --simulate

# Apply after reviewing
dvc api exec-plan

3. Create a new Custom API

# Create a new JSON catalog file manually (see structure below)
# e.g. .cache/customapis/ccsm_NewApi.json

# Set the API as active even before it exists in Dataverse
dvc api use -n ccsm_NewApi

# Validate the local definition before planning
dvc api validate

# Create a sync plan
dvc api plan

# Dry run first
dvc api exec-plan --simulate

# Apply after reviewing
dvc api exec-plan

Why validate before plan?

api validate is a local-only check — no network call, instant feedback. It catches issues that Dataverse would reject with a 400 error (e.g. logicalEntityName on an EntityCollection parameter, missing publisher prefix, invalid type values).

api plan runs the same validation internally and will block if blocking errors are found. Running api validate first gives you the full structured diagnostic report — including non-blocking warnings and informational hints — before attempting to build the plan.

Custom API JSON structure

The catalog file written by api export and read by api plan / api validate:

{
  "schemaVersion": "1.0",
  "source": {
    "exportedAtUtc": "2025-01-15T10:00:00.000Z",
    "environmentUrl": "https://your-org.crm.dynamics.com"
  },
  "customApis": [
    {
      "uniqueName": "ccsm_MyCustomApi",
      "name": "ccsm_MyCustomApi",
      "displayName": "My Custom API",
      "description": "Processes a survey recipient record.",
      "bindingType": "Global",
      "isFunction": false,
      "isPrivate": false,
      "workflowSdkStepEnabled": false,
      "allowedCustomProcessingStepType": "None",
      "requestParameters": [
        {
          "uniqueName": "RecipientId",
          "name": "ccsm_MyCustomApi.RecipientId",
          "displayName": "Recipient ID",
          "description": "ID of the recipient record.",
          "type": "EntityReference",
          "logicalEntityName": "ccsm_surveyrecipient",
          "isOptional": false
        }
      ],
      "responseProperties": [
        {
          "uniqueName": "Success",
          "name": "ccsm_MyCustomApi.Success",
          "displayName": "Success",
          "description": "Whether the operation succeeded.",
          "type": "Boolean"
        }
      ]
    }
  ]
}

Field notes:

Field Values / constraints
bindingType "Global", "Entity", "EntityCollection"
allowedCustomProcessingStepType "None", "AsyncOnly", "SyncAndAsync"
type (parameter/property) "Boolean", "DateTime", "Decimal", "Entity", "EntityCollection", "EntityReference", "Float", "Integer", "Money", "Picklist", "String", "StringArray", "Guid"
logicalEntityName Only set for Entity or EntityReference; must be absent for all other types
name (parameter/property) Recommended format: {customApiUniqueName}.{childUniqueName}
uniqueName (Custom API) Must include publisher prefix, e.g. ccsm_MyApi; only letters, digits, underscores

Publishing notes

The package is built from TypeScript. The repository does not need committed dist/ files; they are generated during build and included in the published npm package.

Before publishing, verify the package contents:

npm run build
npm pack --dry-run

The dry run should include at least:

dist/
src/
README.md
LICENSE
package.json

For scoped public packages:

npm publish --access public

Requirements

  • Node.js 16+
  • npm
  • Access to a Dataverse environment
  • Azure AD / Microsoft Entra ID App Registration with the correct permissions
  • Sufficient Dataverse privileges to read or modify Custom API metadata

License

MIT

Author and contact

Andreas Brunsmann


Dataverse Custom API CLI

Eine Node.js/TypeScript-basierte CLI zum Verwalten von Dataverse Custom APIs. Sie ermöglicht das Lesen, Exportieren, Bearbeiten, Validieren, Vergleichen, Planen und Synchronisieren von Custom-API-Definitionen zwischen lokalen JSON-Dateien und Dataverse-Umgebungen.

Installation

npm install -g @brunsforge/dataverse-custom-api

Übersicht der Features

  • Verbindung zu Dataverse-Umgebungen: Device Code, Client Secret, Interactive Browser
  • Environment-Management: speichern, auflisten, wechseln, entfernen
  • Custom API-Verwaltung:
    • vorhandene Custom APIs auflisten
    • als lokale JSON-Artefakte exportieren
    • lokal bearbeiten
    • lokale Definition validieren — erkennt Typfehler, fehlende Felder und ungültige Kombinationen mit strukturierten Diagnostics
    • mit Dataverse-Metadaten vergleichen
    • Sync-Pläne erstellen (Validierung läuft automatisch; Plan wird bei Fehlern blockiert)
    • einzelne Sync-Operationen ausführen
    • vollständige Sync-Pläne ausführen
  • Simulationsmodus: Operationen mit --simulate vorab prüfen
  • JSON und menschenlesbare Ausgaben: alle Befehle unterstützen --json

Achtung: Befehle, die ohne --simulate ausgeführt werden, können Custom-API-Metadaten in der verbundenen Dataverse-Umgebung erstellen, ändern oder löschen.

Verbindung

dvc connect -u "https://your-org.crm.dynamics.com"
Connected and cached: https://your-org.crm.dynamics.com
Auth mode: deviceCode

Environment-Management

dvc env list       # alle gespeicherten Environments auflisten
dvc env current    # aktives Environment anzeigen
dvc env use -i <id>     # Environment wechseln
dvc env remove -i <id>  # Environment entfernen

Custom API-Management

Auflisten und auswählen

dvc api list               # alle Custom APIs auflisten
dvc api current            # aktive API anzeigen
dvc api use -n ccsm_MyApi  # API als aktiv setzen

Exportieren

dvc api export
dvc api export -n ccsm_MyApi

Lokale Definition validieren

Prüft die lokale JSON-Datei gegen Dataverse-Regeln. Keine Netzwerkverbindung erforderlich.

dvc api validate
dvc api validate -n ccsm_MyApi

Ausgabe ohne Probleme:

Validation for: ccsm_MyApi
Status: succeeded
No issues found.

Ausgabe mit Fehlern:

Validation for: ccsm_MyApi
Status: validationFailed

ERROR [CCDV_PARAM_LOGICAL_ENTITY_NAME_NOT_ALLOWED] [ccsm_MyApi > NewParameter].logicalEntityName
      Request parameter 'NewParameter' has type 'EntityCollection' which does not support logicalEntityName.
      Fix: Remove logicalEntityName or change the parameter type to Entity or EntityReference.

Summary: 1 error(s), 0 warning(s), 0 info(s)

Mit --json wird ein vollständiges CcdvCommandResult-Envelope zurückgegeben (für VS-Code-Extension-Integration).

Geprüfte Regeln (Auswahl):

  • uniqueName: Pflichtfeld, max. 128 Zeichen, muss Publisher-Präfix enthalten (z. B. ccsm_), nur Buchstaben/Ziffern/Unterstriche
  • name und displayName: Pflichtfeld, max. 100 Zeichen
  • description: max. 300 Zeichen
  • bindingType: Global, Entity oder EntityCollection
  • boundEntityLogicalName: Pflicht bei Entity/EntityCollection; muss fehlen bei Global
  • logicalEntityName an Parametern/Properties: nur erlaubt bei Type Entity oder EntityReference
  • Functions (isFunction: true): mindestens eine Response Property erforderlich
  • Functions: keine offenen Entity-/EntityCollection-Request-Parameter (ohne logicalEntityName)

Hinweis: api plan läuft dieselbe Validierung intern ab. Bei blockierenden Fehlern wird kein Plan erstellt; die Fehlermeldung verweist auf api validate für den vollständigen Report.

Vergleichen

dvc api diff
dvc api diff -n ccsm_MyApi

Sync-Plan erstellen

dvc api plan
dvc api plan -n ccsm_MyApi
Plan created: .cache/customapis/ccsm_MyApi.syncplan.json
State file: .cache/customapis/ccsm_MyApi.syncstate.json
Operations: 2
Destructive changes required: no
- [10] createRequestParameter NewParameter (new)
- [20] updateCustomApi ccsm_MyApi (changed)

Ausführen

dvc api exec-plan --simulate   # Dry Run
dvc api exec-plan              # live ausführen

Einzelne Operation ausführen

dvc api exec-op -o "<operationId>" --simulate
dvc api exec-op -o "<operationId>"

Metadaten-Konsistenz prüfen

dvc api check-metadata

Lokale Artefakte entfernen

dvc api remove
dvc api remove -n ccsm_MyApi

Typischer Workflow

Vorhandene Custom API bearbeiten

dvc api list
dvc api use -n ccsm_ExistingApi

# Aktuelle Definition aus Dataverse holen
dvc api export

# JSON-Datei lokal bearbeiten (z. B. Parameter hinzufügen)

# Lokale Definition validieren — Fehler beheben, bevor der Plan erstellt wird
dvc api validate

# Diff mit Dataverse prüfen
dvc api diff

# Sync-Plan erstellen (validiert intern)
dvc api plan

# Dry Run
dvc api exec-plan --simulate

# Live ausführen nach Prüfung
dvc api exec-plan

Neue Custom API erstellen

# Neue JSON-Katalog-Datei erstellen (siehe Struktur unten)
# z. B. .cache/customapis/ccsm_NewApi.json

dvc api use -n ccsm_NewApi

# Lokale Definition validieren, bevor der Plan erstellt wird
dvc api validate

# Sync-Plan erstellen
dvc api plan

# Dry Run
dvc api exec-plan --simulate

# Live ausführen
dvc api exec-plan

Warum vor dem Plan validieren?

api validate ist ein rein lokaler Check — keine Netzwerkverbindung, sofortiges Feedback. Es erkennt alle Probleme, die Dataverse mit einem 400-Fehler ablehnen würde, sowie Warnungen und Hinweise zu empfohlenen Formaten.

api plan läuft dieselbe Validierung intern ab und blockiert bei blockierenden Fehlern. Mit api validate vorab erhältst du den vollständigen strukturierten Report — inklusive Warnungen und Infos — bevor du versuchst, einen Plan zu erstellen.

JSON-Struktur der Custom API

{
  "schemaVersion": "1.0",
  "source": {
    "exportedAtUtc": "2025-01-15T10:00:00.000Z",
    "environmentUrl": "https://your-org.crm.dynamics.com"
  },
  "customApis": [
    {
      "uniqueName": "ccsm_MyCustomApi",
      "name": "ccsm_MyCustomApi",
      "displayName": "My Custom API",
      "description": "Processes a survey recipient record.",
      "bindingType": "Global",
      "isFunction": false,
      "isPrivate": false,
      "workflowSdkStepEnabled": false,
      "allowedCustomProcessingStepType": "None",
      "requestParameters": [
        {
          "uniqueName": "RecipientId",
          "name": "ccsm_MyCustomApi.RecipientId",
          "displayName": "Recipient ID",
          "description": "ID of the recipient record.",
          "type": "EntityReference",
          "logicalEntityName": "ccsm_surveyrecipient",
          "isOptional": false
        }
      ],
      "responseProperties": [
        {
          "uniqueName": "Success",
          "name": "ccsm_MyCustomApi.Success",
          "displayName": "Success",
          "description": "Whether the operation succeeded.",
          "type": "Boolean"
        }
      ]
    }
  ]
}

Voraussetzungen

  • Node.js 16+
  • npm
  • Zugriff auf eine Dataverse-Umgebung
  • Korrekte Azure AD / Microsoft Entra ID App Registration
  • Ausreichende Dataverse-Berechtigungen

Lizenz

MIT

Autor und Kontakt

Andreas Brunsmann