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

@xrmforge/webapi

v0.1.2

Published

Type-safe Xrm.WebApi client with query builder for Dynamics 365

Readme

@xrmforge/webapi

npm version license

A type-safe wrapper around Xrm.WebApi with a fluent query builder. Returns your generated entity interfaces instead of any, raises structured WebApiErrors, and builds OData query strings from generated Fields enums -- no raw strings, no unknown casts.

Part of XrmForge. Browser-safe; import it in D365 form scripts.


Installation

npm install @xrmforge/webapi

Requirements: @types/xrm (>= 9.0.0) as a peer dependency, and a running D365 client context (Xrm.WebApi must be available).


CRUD with typed results

Pass a generated entity interface as the type parameter and get a fully typed result back:

import { webApi, query } from '@xrmforge/webapi';
import type { Account } from '../../generated/entities/account.js';
import { AccountFields } from '../../generated/fields/account.js';

// retrieve<T>(entityName, id, queryOrString?)
const account = await webApi.retrieve<Account>('account', id,
  query.select(AccountFields.Name, AccountFields.City));
account.name;          // string | null (typed, not unknown)

// create -> returns the new record's GUID
const newId = await webApi.create('account', { name: 'Contoso Ltd' });

// update / remove
await webApi.update('account', id, { name: 'Contoso GmbH' });
await webApi.remove('account', id);

The same functions are also exported individually (retrieve, retrieveMultiple, create, update, remove) if you prefer named imports over the webApi namespace object.

Retrieving multiple records (with pagination)

retrieveMultiple returns only the first page by default (up to 5000 records), which is backwards-compatible and avoids accidental full-table scans. Opt into more pages with maxPages:

import { retrieveMultiple } from '@xrmforge/webapi';

const firstPage = await retrieveMultiple<Account>('account', query.top(50));

const all = await retrieveMultiple<Account>('account',
  query.filter(`${AccountFields.City} eq 'Berlin'`),
  { maxPages: Infinity });   // follow every nextLink

Query builder

A fluent, chainable builder for OData query strings. Every method returns the builder, and .build() produces the ?$... string (it is also accepted directly by the CRUD functions).

import { query } from '@xrmforge/webapi';
import { AccountFields } from '../../generated/fields/account.js';

const q = query
  .select(AccountFields.Name, AccountFields.City)
  .filter(`${AccountFields.City} ne null`)
  .orderBy(AccountFields.Name)          // default direction: asc
  .top(50)
  .expand('primarycontactid', ['fullname', 'emailaddress1']);

q.build();
// "?$select=name,address1_city&$filter=address1_city ne null&$orderby=name asc&$top=50&$expand=primarycontactid($select=fullname,emailaddress1)"

| Builder method | OData clause | |----------------|--------------| | select(...fields) | $select | | filter(expr) | $filter (multiple calls combined with and) | | orderBy(field, 'asc' \| 'desc') | $orderby | | top(n) | $top | | expand(nav, subSelect?) | $expand | | build() / toString() | the final query string |

Start a query with query.select(...), query.filter(...), query.top(...), or query.expand(...); create an empty one with createQuery(); or instantiate new QueryBuilder() directly.


Error handling

All operations throw a WebApiError on failure, and on invalid arguments such as a missing entityName or id. It carries message, statusCode (HTTP status), errorCode (the Dataverse error code), and an optional innerMessage:

import { WebApiError } from '@xrmforge/webapi';

try {
  await webApi.retrieve<Account>('account', id);
} catch (err) {
  if (err instanceof WebApiError) {
    console.error(err.statusCode, err.errorCode, err.message);
  }
}

Exports

webApi, retrieve, retrieveMultiple, create, update, remove, QueryBuilder, createQuery, query, WebApiError, and the type RetrieveMultipleOptions.

Documentation

Full guide: XrmForge on GitHub.

License

MIT (c) XrmForge Contributors.