In this blog post, we’ll explore how to integrate Microsoft Dataverse, formerly known as the Common Data Service (CDS), with a Node.js backend and a React.js frontend. Microsoft Dataverse is a cloud-based data storage service provided by Microsoft as part of the Power Platform suite. By integrating Dataverse with Node.js and React.js, we can build powerful applications that leverage the capabilities of Dataverse for data storage and management.
While the article guides you through the import functionality of the code, you can download the project to see the full source code and test the functionality for yourself.
Understanding the Tech Stack:
React Frontend: React.js has emerged as a leading JavaScript library for building user interfaces. Its component-based architecture and virtual DOM make it ideal for creating dynamic and responsive web applications. With React, developers can build interactive UI components and efficiently manage state across the application. Learn more about React.js
Node.js Backend: Node.js, a server-side JavaScript runtime, offers a powerful platform for building scalable and performant backend services. Its event-driven, non-blocking I/O model enables developers to handle concurrent requests efficiently. With Node.js, developers can build RESTful APIs, perform data processing tasks, and integrate with various data sources. Learn more about Node.js
Dataverse integration: Microsoft Dataverse is a comprehensive cloud-based data storage service provided by Microsoft, offering a scalable and secure solution for storing and managing structured business data. Formerly known as the Common Data Service (CDS), Dataverse serves as a central repository for organizing and accessing business-critical information across various applications and services within the Microsoft Power Platform suite. Learn more about Dataverse
Setting Up the Development Environment
1. Required Tools and Software:
- Node.js and npm: Install from nodejs.org.
- React.js: Create a new React project using Create React App.
- Dataverse API : Ensure you have access to a Dataverse API.
2. Installing Node.js and npm:
First, check if Node.js and npm are installed on your system:
# Check if Node.js and npm are installed
node -v
npm -v
# If not installed, download and install from nodejs.org
If not installed, download and install Node.js from nodejs.org.
3. Creating a React Project:
npx create-react-app dataverse-react-integration
cd dataverse-react-integration
3. Initializing a Node.js Project:
Create a backend directory and initialize a Node.js project within it:
mkdir backend
cd backend
npm init -y
npm install express nodemon axios cors dotenv
Edit the package.json file to replace the start script:
"scripts": {
"start": "nodemon src/index.js"
}
4. Creating the Backend:
Register the model-driven app in Azure AD, and obtain the client_id, client_secret, and tenant_id. Ensure that you grant CRUD (Create, Read, Update, Delete) access in Azure.
Create a .env file:
PORT=5000
tenantId="<Enter valid tenant Id>"
clientId="<Enter valid client Id>"
client_secret="<Enter valid client secret>"
scope="<environment-url>/.default"
Create src/index.js:
const express = require('express');
const cors = require('cors');
const {
getFundingRequests
} = require('./controllers');
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
// Use body-parser middleware
// Define routes
app.get('/api/fundingRequests/:name', getFundingRequests);
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Create src/routes.js:
const express = require('express');
const { getFundingRequests } = require('./controllers');
const router = express.Router();
// Define routes
router.get('/api/fundingRequests/:name', getFundingRequests);
module.exports = {
setupRoutes: (app) => {
app.use(router);
},
};
Create src/controllers.js file and implement functionality to retrieve Dataverse data.
A. Fetch Dataverse Access Token
First, we need to authenticate with Dataverse to get an access token. This token is required for subsequent API calls to Dataverse.
const getDataverseAccessToken = async () => {
// Check if the current access token is still valid
if (DataverseaccessToken && Date.now() < tokenExpirationTime) {
return DataverseaccessToken; // Return the existing token if it's still valid
}
try {
// Retrieve environment variables from process.env
const clientId = process.env.clientId;
const clientSecret = process.env.client_secret;
const tenantId = process.env.tenantId;
const resource = process.env.scope;
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
// Send POST request to token endpoint to obtain access token
const response = await axios.post(
tokenEndpoint,
new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope: resource,
})
);
// Store the access token and its expiration time
DataverseaccessToken = response.data.access_token;
tokenExpirationTime = Date.now() + response.data.expires_in * 1000; // Convert seconds to milliseconds
// Return the access token
return DataverseaccessToken;
} catch (error) {
console.error('Error fetching access token:', error.response?.status, error.response?.data);
throw new Error('Error fetching access token');
}
};
This function checks if an existing token is still valid. If not, it requests a new token from the Microsoft identity platform.
B. Get Funding Request from Dataverse
Next, we need to retrieve the funding requests of a specific entity in Dataverse.
const getFundingRequests = async (req, res) => {
try {
const { name } = req.params;
const accessToken = await getDataverseAccessToken(); // Always ensure you have a valid token
const apiUrl = `${process.env.DataverseURL}/dyn_fundingrequests(${name})`;
const response = await axios.get(apiUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const fundprogramId = response.data._dyn_fundingprogramid_value;
const contactId = response.data._dyn_contactid_value;
const programResponse = await axios.get(`${process.env.DataverseURL}/dyn_fundingprograms(${fundprogramId})`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const programCode = programResponse.data.dyn_programcode;
const contactResponse = await axios.get(`${process.env.DataverseURL}/contacts(${contactId})`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const fullName = contactResponse.data.fullname;
// Construct the response object including fullname and programcode
const responseData = {
...response.data,
Applicantfullname: fullName,
Fundingprogramcode: programCode,
};
// Send the response including fullname and programcode
res.json(responseData);
} catch (error) {
console.error('Error fetching funding requests:', error.response?.status, error.response?.data);
res.status(500).json({ error: 'Internal server error' });
}
};
5. Creating the React Frontend:
In your React project, install the necessary dependencies:
npm install @emotion/styled @mui/icons-material @mui/material axios
Use the following code to retrieve the Dataverse data from the backend:
const getfundingrequests = async () => {
try {
const apiUrl = `http://localhost:5000/api/fundingRequests/${name}`;
const fundingResponse = await api.get(apiUrl);
const fundingData = fundingResponse.data;
setRequest(fundingData)
} catch (error) {
console.error('Error fetching funding request:', error);
}
};
Add the Box, Grid, TextField, Typography, Paper component from Material-UI into the React component to display funding request data from dataverse in a form:
<Box
component="form"
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center', // Center align the form
}}
noValidate
autoComplete="off"
>
<Grid container spacing={3}>
{/* First Column */}
<Grid item xs={12} sm={6}>
<Paper elevation={3} sx={{ padding: 2, backgroundColor: '#ffffff', boxShadow: '0px 3px 6px #00000029' }}>
<TextField
required
fullWidth
id="outlined-required-request-number"
label="Request Number"
value={requestData.dyn_requestnumber}
sx={{ marginBottom: 2 }} // Add space below the TextField
/>
<TextField
fullWidth
required
id="outlined-required-funding-program"
label="Funding Program"
value={requestData.Fundingprogramcode}
sx={{ marginBottom: 2 }} // Add space below the TextField
/>
<TextField
fullWidth
required
id="outlined-required-applicant"
label="Applicant"
value={requestData.Applicantfullname}
sx={{ marginBottom: 2 }} // Add space below the TextField
/>
<TextField
fullWidth
required
id="outlined-required-request-amount"
label="Request Amount"
value={requestData.dyn_requestedamount}
sx={{ marginBottom: 2 }} // Add space below the TextField
/>
<TextField
fullWidth
id="outlined-required-status-reason"
label="Status Reason"
value={getStatusLabel(requestData.statuscode)}
/>
</Paper>
</Grid>
{/* Second Column */}
<Grid item xs={12} sm={6}>
<Paper elevation={3} sx={{ padding: 2, backgroundColor: '#ffffff', boxShadow: '0px 3px 6px #00000029' }}>
<Typography variant="h6" gutterBottom sx={{ textAlign: 'left' }}>
Payment Summary
</Typography>
<TextField
required
fullWidth
id="filled-required-awarded-amount"
label="Awarded Amount"
value={requestData.dyn_awardedamount}
sx={{ marginTop: 2 }}
/>
<TextField
fullWidth
id="filled-read-only-total-amount-paid"
label="Total Amount Paid"
value={requestData.dyn_totalamountpaid}
sx={{ marginTop: 2 }}
/>
<TextField
fullWidth
id="filled-helper-text-total-amount-paid"
label="Total Amount Scheduled"
value={requestData.dyn_totalamountscheduled}
sx={{ marginTop: 2 }}
/>
</Paper>
</Grid>
</Grid>
</Box>

6. Running the Application:
Start the Node.js server:
cd backend
npm start
Start the React development server:
cd ..
npm start
Conclusion:
In this blog post, we’ve explored how to retrieve data from Microsoft Dataverse in React pages via Node.js backend. This approach provides a robust solution for managing data efficiently and seamlessly within an organization. By combining the power of React, Node.js, and Dataverse, developers can create dynamic and interactive applications that streamline data management processes and enhance collaboration.
Feel free to download the project and explore the source code for a deeper understanding of the implementation. Happy coding!
