Skip to content

Webresources

pete-mc edited this page Jul 7, 2026 · 9 revisions

Web Resources

The Web Resources project type lets you build Dataverse web resources in TypeScript, with strong typing, bundling, testing, and one-click deploy:

  • Write web resources as TypeScript classes.
  • Bundle to a single JavaScript library with webpack.
  • Deploy straight to Dataverse via the Web API (no external tooling).
  • Generate strongly-typed Xrm typings with XrmDefinitelyTyped.
  • Unit test with Jest and xrm-mock.

Web resource menu

Create New Class

Right-click the webresources_src folder (or use the menu) and choose Create New Class. You'll be asked for the class name, the Dataverse table, and the form the code runs on. The library.ts entry point is updated to include the new class so webpack bundles it.

export class Account {
  static async OnLoad(executionContext: Xrm.ExecutionContext<unknown, unknown>): Promise<void> {
    const form = <Form.account.Main.Information>executionContext.getFormContext();
    this.BindEvents(form);
    this.OnLoadLogic(form);
  }

  static async OnLoadLogic(form: Form.account.Main.Information) {
    switch (form.ui.getFormType().valueOf()) {
      case Xrm.FormType.Create: /* new records */ break;
      case Xrm.FormType.Update: /* existing records */ break;
    }
  }

  static async BindEvents(form: Form.account.Main.Information) {
    form.data.entity.addOnSave(() => this.OnSave(form));
  }

  static async OnSave(form: Form.account.Main.Information) { /* on save */ }
}

Casting the form as Form.account.Main.Information gives full IntelliSense for the fields on that form — see Generate Typings.

Generate Typings

Generate Typings produces strongly-typed definitions for the Web API and each form's FormContext (via XrmDefinitelyTyped) into a typings folder. Re-run it whenever the schema or a form's fields change.

XrmDefinitelyTyped currently runs on Windows only.

Build & Deploy

  • Build Web Resources — webpack bundles the TypeScript into prefix_bundle.js (with a source map for debugging).
  • Build & Deploy Web Resources — builds, then upserts every file in bin/ directly to Dataverse via the Web API and publishes. If a solution is configured (webresourceSolutionName), the resources are added to it.

Calling the code from a form

Add the bundle as a web resource on the form and register the function:

  • Name: prefix_bundle.js
  • Function: prefix.ClassName.OnLoad

Or let the extension do it — see below.

Register form events

Instead of wiring things up in the Dataverse UI, use Add Form Registration to insert a PowerTools.RegisterEvent block into your class, then Register Form Events to add the library and register the handlers on the form automatically.

<PowerTools.RegisterEvent[]>[
  {
    formId: "f182e4c6-6bff-ed11-8f6d-00224897cd92",
    event: "onload",
    executionContext: true,
    triggerId: "d8b98c6a-d2a3-4465-bfec-aa75adf73c15",
    function: "prefix.Account.OnLoad",
  },
];

Form Intersections

Use the Form Intersects view to define typings that target multiple similar forms at once (see the XrmDefinitelyTyped Form intersections docs). Click the + to add an intersect, then add the tables/forms to it, and re-generate typings.

Testing

Create tests with Create New Test. Tests use Jest and xrm-mock as a fake Xrm implementation, so web resources run locally:

import { Account } from "../Account";
import { XrmMockGenerator } from "xrm-mock";

XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("someattribute", "somevalue");
const form = XrmMockGenerator.eventContext.getFormContext() as Form.account.Main.Information;
Account.OnLoad(XrmMockGenerator.eventContext);

it("keeps the value", () => {
  expect(form.getAttribute("someattribute").getValue()).toBe("somevalue");
});

XrmQuery

Alongside form typings, the project bundles XrmQuery — a fluent, strongly-typed wrapper over the Web API:

const accounts = await XrmQuery.retrieveMultiple(x => x.accounts)
  .select(x => [x.accountnumber])
  .filter(x => Filter.equals(x.name, "Contoso"))
  .promise();

Migrating from spkl

Older web resource projects used spkl for deployment. If a spkl.json is present, run Upgrade from Spkl — it reads the solution name into dataverse-powertools.json and removes the old spkl config. Deployment then uses the built-in Web API upsert described above.

Clone this wiki locally