npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mohsinonxrm/dataverse-sdk-batch

v1.0.0

Published

Batch operations support for the Dataverse SDK, enabling multiple requests to be executed in a single HTTP call with support for transactional changesets.

Readme

@mohsinonxrm/dataverse-sdk-batch

Batch operations support for the Dataverse SDK, enabling multiple requests to be executed in a single HTTP call with support for transactional changesets.

Features

  • Batch Multiple Requests: Combine up to 1000 requests in a single HTTP call
  • Transactional Changesets: Group related modifications that must succeed or fail together
  • Content-ID References: Reference earlier requests in changesets using $1, $2 syntax
  • Error Handling: Comprehensive error handling with per-request error tracking
  • Continue on Error: Optional continue-on-error semantics for partial success scenarios
  • Auto-Split Detection: Prevents exceeding Dataverse's 1000 request limit

Installation

pnpm add @mohsinonxrm/dataverse-sdk-batch

Quick Start

import { DataverseClient } from "@mohsinonxrm/dataverse-sdk-core";
import { BatchRequestBuilder } from "@mohsinonxrm/dataverse-sdk-batch";

const client = new DataverseClient({
  /* config */
});
const batch = new BatchRequestBuilder(client);

// Add individual requests
batch.addRequest("getAccounts", "GET", "/accounts?$top=5");
batch.addRequest("getContacts", "GET", "/contacts?$top=5");

// Execute the batch
const result = await batch.execute();

// Access responses
for (const [id, response] of result.responses) {
  console.log(`Request ${id}:`, response.body);
}

API Reference

BatchRequestBuilder

Constructor

constructor(client: DataverseClient)

Creates a new batch request builder instance.

  • client: DataverseClient instance for executing the batch

Methods

addRequest(id, method, url, headers?, body?, dependsOn?)

Adds a request to the batch.

addRequest(
  id: string,
  method: string,
  url: string,
  headers?: Record<string, string>,
  body?: any,
  dependsOn?: string[]
): BatchRequestBuilder
  • id: Unique identifier for this request
  • method: HTTP method (GET, POST, PATCH, DELETE, etc.)
  • url: Request URL (relative to API base or absolute)
  • headers: Optional HTTP headers
  • body: Optional request body (will be JSON stringified if object)
  • dependsOn: Optional array of request IDs this request depends on

Returns the builder for method chaining.

beginChangeset()

Starts a new transactional changeset. All requests added after this call will be part of the changeset until endChangeset() is called.

beginChangeset(): BatchRequestBuilder

Changesets provide transactional semantics - either all requests succeed or all fail together.

endChangeset()

Ends the current changeset.

endChangeset(): BatchRequestBuilder

Throws an error if no changeset is active or if the changeset is empty.

execute(options?)

Executes the batch request.

execute(options?: BatchExecutionOptions): Promise<BatchExecutionResult>

Options:

  • continueOnError: Continue processing remaining requests on error (default: false)
  • batchSize: Size for auto-splitting (not yet implemented)
  • headers: Additional HTTP headers to include

Returns: BatchExecutionResult with responses and errors

getTotalRequestCount()

Returns the total number of requests in the batch (including those in changesets).

getTotalRequestCount(): number
clear()

Clears all requests and resets the builder state.

clear(): void

Usage Examples

Individual Requests

Combine multiple read operations in a single HTTP call:

const batch = new BatchRequestBuilder(client);

batch
  .addRequest("accounts", "GET", "/accounts?$select=name&$top=10")
  .addRequest("contacts", "GET", "/contacts?$select=fullname&$top=10")
  .addRequest("leads", "GET", "/leads?$select=fullname&$top=10");

const result = await batch.execute();

// Process responses
const accounts = result.responses.get("accounts")?.body;
const contacts = result.responses.get("contacts")?.body;
const leads = result.responses.get("leads")?.body;

Transactional Changesets

Group related create/update/delete operations:

const batch = new BatchRequestBuilder(client);

// Create account and related contacts in a transaction
batch.beginChangeset();

batch.addRequest(
  "createAccount",
  "POST",
  "/accounts",
  {},
  {
    name: "Contoso Ltd",
    telephone1: "555-0100",
  }
);

batch.addRequest(
  "createContact1",
  "POST",
  "/contacts",
  {},
  {
    firstname: "John",
    lastname: "Doe",
    "[email protected]": "$1", // Reference account created above
  }
);

batch.addRequest(
  "createContact2",
  "POST",
  "/contacts",
  {},
  {
    firstname: "Jane",
    lastname: "Smith",
    "[email protected]": "$1", // Reference same account
  }
);

batch.endChangeset();

const result = await batch.execute();

Content-ID References

Use Content-ID syntax to reference earlier requests in changesets:

batch.beginChangeset();

// Content-ID: 1
batch.addRequest(
  "account",
  "POST",
  "/accounts",
  {},
  {
    name: "Adventure Works",
  }
);

// Content-ID: 2 - references Content-ID 1
batch.addRequest(
  "contact",
  "POST",
  "/contacts",
  {},
  {
    firstname: "Nancy",
    lastname: "Davolio",
    "[email protected]": "$1",
  }
);

// Content-ID: 3 - references Content-ID 2
batch.addRequest(
  "task",
  "POST",
  "/tasks",
  {},
  {
    subject: "Follow up",
    "[email protected]": "$2",
  }
);

batch.endChangeset();

Error Handling

Handle errors with comprehensive per-request tracking:

try {
  const result = await batch.execute();

  if (!result.success) {
    console.log(`${result.failedRequests} of ${result.totalRequests} requests failed`);
  }
} catch (error) {
  if (error instanceof DataverseBatchError) {
    console.log(`Successful: ${error.successfulRequests}`);
    console.log(`Failed: ${error.failedRequests}`);

    // Check specific request errors
    for (const requestId of error.getFailedRequestIds()) {
      const failedResponse = error.getErrorForRequest(requestId);
      console.log(`Request ${requestId} failed:`, failedResponse?.body);
    }
  }
}

Continue on Error

Process partial successes with continue-on-error semantics:

const batch = new BatchRequestBuilder(client);

batch
  .addRequest("req1", "POST", "/accounts", {}, { name: "Valid Account" })
  .addRequest(
    "req2",
    "POST",
    "/accounts",
    {},
    {
      /* invalid data */
    }
  )
  .addRequest("req3", "POST", "/accounts", {}, { name: "Another Valid Account" });

try {
  const result = await batch.execute({ continueOnError: true });
} catch (error) {
  if (error instanceof DataverseBatchError) {
    // error.successfulRequests will be 2
    // error.failedRequests will be 1

    // Process successful responses
    for (const [id, response] of error.requestErrors) {
      if (response.status >= 200 && response.status < 300) {
        console.log("Success:", response.body);
      } else {
        console.log("Failed:", response.body);
      }
    }
  }
}

Mixed Requests and Changesets

Combine individual requests with transactional changesets:

const batch = new BatchRequestBuilder(client);

// Individual read request
batch.addRequest("getConfig", "GET", "/organizations");

// Transactional changeset
batch.beginChangeset();
batch.addRequest("createAccount", "POST", "/accounts", {}, { name: "Test" });
batch.addRequest("createContact", "POST", "/contacts", {}, { firstname: "John" });
batch.endChangeset();

// Another individual request
batch.addRequest("getAccounts", "GET", "/accounts?$top=5");

const result = await batch.execute();

Important Notes

Request Limits

  • Maximum batch size: 1000 requests per batch (Dataverse limit)
  • Default batch size: 10 requests (for auto-split, not yet implemented)
  • Exceeding 1000 requests will throw an error

Changeset Behavior

  • Transactional: All requests in a changeset succeed or fail together
  • Content-IDs: Assigned sequentially (1, 2, 3...) within each changeset
  • Cannot nest: Only one changeset can be active at a time
  • Cannot be empty: Must contain at least one request

Content-ID References

  • Use $<number> syntax to reference earlier requests (e.g., $1, $2)
  • Only valid within the same changeset
  • Commonly used in @odata.bind properties for associations
  • Content-IDs are sequential starting from 1

Error Handling

  • By default, batch execution stops on first error
  • Use continueOnError: true to process all requests
  • DataverseBatchError provides detailed per-request error information
  • Check result.success to determine if all requests succeeded

Types

BatchExecutionOptions

interface BatchExecutionOptions {
  continueOnError?: boolean;
  batchSize?: number;
  headers?: Record<string, string>;
}

BatchExecutionResult

interface BatchExecutionResult {
  responses: Map<string, BatchResponseStep>;
  errors: Map<string, Error>;
  success: boolean;
  totalRequests: number;
  successfulRequests: number;
  failedRequests: number;
}

BatchResponseStep

interface BatchResponseStep {
  id: string;
  status: number;
  statusText: string;
  headers: Record<string, string>;
  body: any;
}

License

GNU AGPL-3.0

Related Packages