編集

次の方法で共有


Quickstart: Web API with client-side JavaScript and Visual Studio Code

This quickstart demonstrates how you can connect to Dataverse and use the Web API with the following technologies:

Technology Description
JavaScript A programming language for web development, enabling interactive content. It runs in browsers for client-side scripting and can be used server-side with Node.js.
Visual Studio Code A lightweight, open-source code editor with debugging, syntax highlighting, and plugin support.
Single Page Applications (SPAs) Web applications that load a single HTML page and dynamically update content as the user interacts with the app. This approach provides a smoother, faster user experience by reducing page reloads and enhancing performance.
Microsoft Authentication Library for JavaScript (MSAL.js) A library that enables authentication and authorization for web applications using Microsoft identity platforms. It simplifies integrating secure sign-in and token acquisition for accessing protected resources.
Cross-Origin Resource Sharing (CORS) A SPA application can use client-side JavaScript with the Dataverse Web API because CORS is enabled. CORS is a security feature in web browsers that allows controlled access to resources on a web server from a different origin. It enables web applications to bypass the same-origin policy, facilitating safe and secure data sharing across different domains.

Goal

This quickstart focuses on connecting to the Dataverse Web API with JavaScript using a SPA client application with a minimum of number of steps. When you complete this quickstart, you're able to:

  • Sign in and connect to Dataverse
  • Invoke the WhoAmI function and display your UserID value.

Completed running quickstart

Completing this quickstart enables you to try the Web API Data operations Samples (Client-side JavaScript) which demonstrate more advanced capabilities.

Note

This quickstart doesn't apply to the following client-side JavaScript scenarios:

Scenario Learn more
Model-driven application scripts - Apply business logic using client scripting in model-driven apps using JavaScript
- Xrm.WebApi (Client API reference)
Power Apps component framework - Code components WebAPI
- Implementing Web API component
Power Pages Portals Power Pages Portals Web API

In these scenarios, the respective application type provides a capability for you to send requests rather than use the JavaScript native Fetch API directly as shown in this quickstart. Client-side scripts within model-driven apps run in the context of an authenticated application, so each request doesn't require an access token.

Prerequisites

The following table describes the prerequisites needed to complete this quickstart and Web API Data operations Samples (Client-side JavaScript).

Prerequisite Description
Privileges to create an Entra App registration You can't complete this quickstart without the ability create a Microsoft Entra app registration to enable it.

If you aren't sure if you can, try the first step to Register a SPA application and find out.
Visual Studio Code If Visual Studio Code isn't installed on your computer, you must Download and install Visual Studio Code to run this quickstart.
Node.js Node.js is a runtime environment that allows you to run JavaScript on the server side. This quickstart creates a SPA application that runs JavaScript on the client side in a browser rather than the Node.js runtime. But Node Package Manager (npm) is installed with Node.js, and you need npm to install Parcel and the MSAL.js library.
Parcel Modern web applications typically have many dependencies on open source libraries distributed using npm and scripts that need to be managed and optimized during the build process. These tools are called 'bundlers'. The most common one is webpack. This quick start uses Parcel because it offers a simplified experience.

For quickstarts and samples that show SPA applications using different frameworks and bundlers, see Microsoft Entra Single-page applications samples. You can adapt these samples to use Dataverse Web API with the information shown in this quickstart.
Web Technologies Knowledge of HTML, JavaScript, and CSS are required to understand how this quickstart works. Understanding how to make network requests with JavaScript is essential.

Register a SPA application

This step is first because if you can't register an app, you can't complete this quick start.

Any of the following privileged Microsoft Entra roles include the required permissions:

When you configure the application, you need an application (client) ID, and the ID of your Microsoft Entra tenant. You should also choose a descriptive name for the application so people know what the application was created for.

Register your app

You can register your application using either the:

Create the application registration

  1. Sign in to the Microsoft Entra admin center.

  2. If you have access to multiple tenants, use the Settings icon in the top menu to switch to the tenant in which you want to register the application from the Directories + subscriptions menu.

  3. Browse to Identity > Applications > App registrations and select New registration.

  4. Enter a Name for the application, such as Dataverse Web API Quickstart SPA.

  5. For Supported account types, under Who can use this application or access this API, select Accounts in this organizational directory only (<Your tenant name> - Single tenant).

  6. For Redirect URI (optional)

    1. For Select a platform, choose Single-page application (SPA).
    2. Enter http://localhost:1234 as the value.
  7. Select Register to save your changes.

  8. In the window for the app registration you created, in the Overview tab, below Essentials, you can find these values:

    • Application (client) ID
    • Directory (tenant) ID
  9. Copy these values because you need them when you create the .env file to use environment variables.

Add Dataverse user_impersonation privilege

  1. In the Manage area, select API permissions.
  2. Select Add a permission.
  3. In the Request API permissions flyout, select the APIs my organization uses tab.
  4. Type 'Dataverse' to find application (client) ID 00000007-0000-0000-c000-000000000000.
  5. Select the Dataverse application.
  6. In Select permissions, user_impersonation is the only available delegated permission. Select it.
  7. Select Add permissions.

Note

If you don't have the privileges to create an app registration for your company, get a tenant of your own through via the Power Apps Developer Plan.

Install Node.js

  1. Go to Download Node.js.

  2. Choose the appropriate installer for your operating system (Windows, macOS, or Linux) and download it.

  3. Run the installer. Make sure you accept the default option to: Install npm, the recommended package manager for Node.js.

  4. Verify the installation by opening a terminal or command prompt, typing these commands and pressing Enter.

    • node -v
    • npm -v

    You should see something like this:

    PS C:\Users\you> node -v
    v22.14.0
    PS C:\Users\you> npm -v
    9.5.0
    PS C:\Users\you>
    

Create a project

Note

You can skip these steps by cloning or downloading the PowerApps-Samples repository. The completed application for these steps is available at /dataverse/webapi/JS/quickspa. Follow the instructions in the README.

The instructions in this section guide you to install dependencies from npm, create the folder structure, open Visual Studio Code.

  1. Open a terminal window to a place where you want to create a project. For these instructions, we use C:\projects.

  2. Type the following commands and press Enter to achieve the following actions:

    Command Action
    mkdir quickspa Create a new folder named quickspa.
    cd quickspa Move into the new quickspa folder.
    npm install --save-dev parcel Install Parcel and initialize the project.
    npm install @azure/msal-browser Install the MSAL.js library.
    npm install dotenv Install dotenv to access environment variables that store potentially sensitive configuration data.
    mkdir src Create a src folder where you add HTML, JS, and CSS files for your app in the following steps.
    code . Open Visual Studio Code in the context of the quickspa folder.

Your project should look like this in Visual Studio Code Explorer:

Shows the newly created quickspa project before any files are added.

Create the .env file

Storing configuration data in the environment separate from code is a security best practice.

  1. Create a new file named .env in the root of your quickspa folder.

  2. Paste in the values from Register your app to replace the CLIENT_ID and TENANT_ID values below.

    # The environment this application will connect to.
    BASE_URL=https://<yourorg>.api.crm.dynamics.com
    # The registered Entra application id
    CLIENT_ID=11112222-bbbb-3333-cccc-4444dddd5555
    # The Entra tenant id
    TENANT_ID=aaaabbbb-0000-cccc-1111-dddd2222eeee
    # The SPA redirect URI included in the Entra application registration
    REDIRECT_URI=http://localhost:1234
    
  3. Set the BASE_URL value to the URL of the Web API URL for the environment you want to connect to.

Note

You won't check-in the .env file. In Create .gitignore file, you will exclude it. But you might want to create a .env.example file using the placeholder values so that people know what data it should contain.

Create an HTML page

The instructions in this section describe how to create the HTML file that provides the user interface for the SPA application.

  1. Create a new file in the src folder named index.html.

  2. Copy and paste this content to the index.html page:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Dataverse Web API JavaScript Quick Start</title>
        <link rel="stylesheet" href="styles/style.css" />
      </head>
      <body>
        <header>
          <h1>Dataverse Web API JavaScript Quick Start</h1>
          <button id="loginButton">Login</button>
          <button id="logoutButton" class="hidden">Logout</button>
        </header>
        <nav id="buttonContainer" class="disabled">
          <button id="whoAmIButton">WhoAmI</button>
        </nav>
        <main id="container"></main>
        <script type="module" src="scripts/index.js"></script>
      </body>
    </html>
    

This HTML provides the following elements:

Element ID Element type Description
loginButton button To open the login dialog.
logoutButton button To open the logout dialog. Hidden by default.
buttonContainer nav Contains buttons that require user to be logged in to use. Disabled by default.
whoAmIButton button Executes the WhoAmI function to display the user's ID.
container main Area where information can be displayed to the user.
script Loads the index.js file after the rests of the elements of the page loads.

Create a JavaScript script

This file contains all the logic that makes the index.html page dynamic.

  1. Create a new folder in the src folder named scripts.

  2. Create a new file in scripts folder named index.js.

  3. Copy and paste this content into the index.js page:

    import { PublicClientApplication } from "@azure/msal-browser";
    import 'dotenv/config'
    
    // Load the environment variables from the .env file
    const config = {
       baseUrl: process.env.BASE_URL,
       clientId: process.env.CLIENT_ID,
       tenantId: process.env.TENANT_ID,
       redirectUri: process.env.REDIRECT_URI,
    };
    
    // Microsoft Authentication Library (MSAL) configuration
    const msalConfig = {
      auth: {
        clientId: config.clientId,
        authority: "https://login.microsoftonline.com/" + config.tenantId,
        redirectUri: config.redirectUri,
        postLogoutRedirectUri: config.redirectUri,
      },
      cache: {
        cacheLocation: "sessionStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: true,
      },
    };
    
    // Create an instance of MSAL
    const msalInstance = new PublicClientApplication(msalConfig);
    
    // body/main element where messages are displayed
    const container = document.getElementById("container");
    
    // Event handler for login button
    async function logIn() {
      await msalInstance.initialize();
    
      if (!msalInstance.getActiveAccount()) {
        const request = {
          scopes: ["User.Read", config.baseUrl + "/user_impersonation"],
        };
        try {
          const response = await msalInstance.loginPopup(request);
          msalInstance.setActiveAccount(response.account);
    
          // Hide the loginButton so it won't get pressed twice
          document.getElementById("loginButton").style.display = "none";
    
          // Show the logoutButton
          const logoutButton = document.getElementById("logoutButton");
          logoutButton.innerHTML = "Logout " + response.account.name;
          logoutButton.style.display = "block";
          // Enable any buttons in the nav element
          document.getElementsByTagName("nav")[0].classList.remove("disabled");
        } catch (error) {
          let p = document.createElement("p");
          p.textContent = "Error logging in: " + error;
          p.className = "error";
          container.append(p);
        }
      } else {
        // Clear the active account and try again
        msalInstance.setActiveAccount(null);
        this.click();
      }
    }
    
    // Event handler for logout button
    async function logOut() {
      const activeAccount = await msalInstance.getActiveAccount();
      const logoutRequest = {
        account: activeAccount,
        mainWindowRedirectUri: config.redirectUri,
      };
    
      try {
        await msalInstance.logoutPopup(logoutRequest);
    
        document.getElementById("loginButton").style.display = "block";
    
        this.innerHTML = "Logout ";
        this.style.display = "none";
        document.getElementsByTagName("nav")[0].classList.remove("disabled");
      } catch (error) {
        console.error("Error logging out: ", error);
      }
    }
    
    /**
     * Retrieves an access token using MSAL (Microsoft Authentication Library).
     * Set as the getToken function for the DataverseWebAPI client in the login function.
     *
     * @async
     * @function getToken
     * @returns {Promise<string>} The access token.
     * @throws {Error} If token acquisition fails and is not an interaction required error.
     */
    async function getToken() {
      const request = {
        scopes: [config.baseUrl + "/.default"],
      };
    
      try {
        const response = await msalInstance.acquireTokenSilent(request);
        return response.accessToken;
      } catch (error) {
        if (error instanceof msal.InteractionRequiredAuthError) {
          const response = await msalInstance.acquireTokenPopup(request);
          return response.accessToken;
        } else {
          console.error(error);
          throw error;
        }
      }
    }
    
    // Add event listener to the login button
    document.getElementById("loginButton").onclick = logIn;
    
    // Add event listener to the logout button
    document.getElementById("logoutButton").onclick = logOut;
    
    /// Function to get the current user's information
    /// using the WhoAmI function of the Dataverse Web API.
    async function whoAmI() {
      const token = await getToken();
      const request = new Request(config.baseUrl + "/api/data/v9.2/WhoAmI", {
        method: "GET",
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
          Accept: "application/json",
          "OData-Version": "4.0",
          "OData-MaxVersion": "4.0",
        },
      });
      // Send the request to the API
      const response = await fetch(request);
      // Handle the response
      if (!response.ok) {
        throw new Error("Network response was not ok: " + response.statusText);
      }
      // Successfully received response
      return await response.json();
    }
    
    // Add event listener to the whoAmI button
    document.getElementById("whoAmIButton").onclick = async function () {
      // Clear any previous messages
      container.replaceChildren();
      try {
        const response = await whoAmI();
        let p1 = document.createElement("p");
        p1.textContent =
          "Congratulations! You connected to Dataverse using the Web API.";
        container.append(p1);
        let p2 = document.createElement("p");
        p2.textContent = "User ID: " + response.UserId;
        container.append(p2);
      } catch (error) {
        let p = document.createElement("p");
        p.textContent = "Error fetching user info: " + error;
        p.className = "error";
        container.append(p);
      }
    };
    

The index.js script contains the following constants and functions:

Item Description
config Contains the data used by the Microsoft Authentication Library (MSAL) configuration.
msalConfig Microsoft Authentication Library (MSAL) configuration.
msalInstance The MSAL PublicClientApplication instance.
container The element where messages are displayed.
getToken Retrieves an access token using MSAL.
logIn Event listener function for the login button. Opens a choose account dialog.
logOut Event listener function for the logout button. Opens a choose account dialog.
whoAmI Asynchronous function that calls the WhoAmI function to retrieve data from Dataverse.
whoAmIButton event listener The function that calls the whoAmI function and manages the UI changes to show the message.

Create a CSS page

The Cascading Style Sheet (CSS) file makes the HTML page more attractive and has a role in controlling when controls are disabled or hidden.

  1. Create a new folder named styles in the src folder.

  2. Create a new file named style.css in the styles folder.

  3. Copy and paste this text into the style.css file:

    .disabled {
       pointer-events: none;
       opacity: 0.5;
       /* Optional: to visually indicate the element is disabled */
    }
    
    .hidden {
       display: none;
    }
    
    .error {
       color: red;
    }
    
    .expectedError {
       color: green;
    }
    
    body {
       font-family: 'Roboto', sans-serif;
       font-size: 16px;
       line-height: 1.6;
       color: #333;
       background-color: #f9f9f9;
    }
    
    h1,
    h2,
    h3 {
       color: #2c3e50;
    }
    
    button {
       background-color: #3498db;
       color: #fff;
       border: none;
       padding: 10px 20px;
       border-radius: 5px;
       box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
       transition: background-color 0.3s ease;
       margin: 5px;
       /* Adjust the value as needed */
    }
    
    button:hover {
       background-color: #2980b9;
    }
    
    header {
       padding-bottom: 10px;
       /* Adjust the value as needed */
    }
    

Create .gitignore file

When your app is checked in with source control, adding a .gitignore file prevents checking in files the specified files and folders.

  1. Create a file named .gitignore.

  2. Add the following content:

    .parcel-cache
    dist
    node_modules
    .env
    

The .parcel-cache and dist folders appear when you run the app for the first time.

Not checking in the .env file is a security best practice. You might want to check in a placeholder .env.sample file with placeholder values.

Your project should look like this in Visual Studio Code Explorer:

Shows the quickspa project after files are added.

Configure your package.json file

Your package.json file should look something like this:

{
  "devDependencies": {
    "parcel": "^2.14.1",
  },
  "dependencies": {
    "@azure/msal-browser": "^4.7.0",
    "dotenv": "^16.4.7"
  }
}

Add this scripts item underneath dependencies:

  "dependencies": {
    "@azure/msal-browser": "^4.7.0",
    "dotenv": "^16.4.7"
  },
  "scripts": {
    "start": "parcel src/index.html"
  }

This configuration allows you to start the application using npm start in the next step.

Try it

  1. In Visual Studio Code, open a terminal window

  2. Type npm start and press Enter.

    Note

    You might see some output written to the terminal while the project initializes for the first time. This is parcel installing some more node modules to mitigate issues when using dotenv. Look at the package.json and you should some new items added to the devDependencies.

    You should expect output to the terminal that looks like this:

    Server running at http://localhost:1234
    Built in 1.08s
    
  3. Press Ctrl + click the http://localhost:1234 link to open your browser.

  4. In your browser, select the Login button.

    The Sign in to your account dialog opens.

  5. In the Sign in to your account dialog, select the account that has access to Dataverse.

    The first time you access using a new application (client) ID value, you see this Permissions requested dialog:

    Permissions requested dialog

  6. Select Accept on the Permissions requested dialog.

  7. Select the WhoAmI button.

    The message Congratulations! You connected to Dataverse using the Web API. is displayed with your UserId value from the WhoAmIResponse complex type.

Trouble shooting

This section contains errors that you might encounter running this quick start.

Note

If you experience issues completing the steps in this quick start, try cloning or downloading the PowerApps-Samples repository. The completed application for these steps is available at /dataverse/webapi/JS/quickspa. Follow the instructions in the README. If that doesn't work, create an GitHub issue referencing this quickspa sample application.

Selected user account doesn't exist in tenant

When the account you select doesn't belong to the same Microsoft Entra tenant as the registered application, you get this error in the Pick an account dialog:

Selected user account does not exist in tenant '{Your tenant name}' and cannot access the application '{Your application ID}' in that tenant. The account needs to be added as an external user in the tenant first. Please use a different account.

Resolution: Make sure you choose the correct user.

Next steps

Try other samples that use client-side JavaScript.

Learn more about Dataverse Web API capabilities by understanding the service documents.