SharePoint Framework - Call Azure Function

Overview

SharePoint Framework client web parts are used to develop visual components in modern and classic SharePoint. In some scenarios, all the processing cannot happen on SharePoint front and we also need to perform certain processing outside of SharePoint. Azure Functions is one of the most widely used options in these scenarios.
 
In this article, we will explore how to create a simple Azure Function and consume it in the SharePoint Framework client web part.
 
Azure Functions
 
Azure Functions is serverless computing. It is an event-driven, compute on demand experience. Follow the below steps to create an Azure Function.
  1. Open Azure Portal https://portal.azure.com.
  2. Click "Create Resource".
  3. Under Compute, select Functions App.

    SharePoint Framework - Call Azure Function

  4. Fill in the information to create the Function App.

    SharePoint Framework - Call Azure Function

  5. Click Create.
  6. Once the Azure function is ready, click Functions > New function.

    SharePoint Framework - Call Azure Function

  7. Select the HTTP trigger.

    SharePoint Framework - Call Azure Function

  8. Type the function name, then select Authorization level.
  9. Click Create.
  10. Type-in the below code in Function App.
    1. #r "Newtonsoft.Json"    
    2.     
    3. using System.Net;    
    4. using Microsoft.AspNetCore.Mvc;    
    5. using Microsoft.Extensions.Primitives;    
    6. using Newtonsoft.Json;    
    7.     
    8. public static async Task<IActionResult> Run(HttpRequest req, ILogger log)    
    9. {    
    10.     log.LogInformation("C# HTTP trigger function processed a request.");    
    11.     
    12.     string name = req.Query["name"];    
    13.     
    14.     string requestBody = await new StreamReader(req.Body).ReadToEndAsync();    
    15.     dynamic data = JsonConvert.DeserializeObject(requestBody);    
    16.     name = name ?? data?.name;    
    17.     
    18.     return name != null    
    19.         ? (ActionResult)new OkObjectResult(new { name = $"Hello, {name}"} )    
    20.         : new BadRequestObjectResult(new { name = "Please pass a name on the query string or in the request body"});    
    21. }   
  11. Click "Run" to test the Azure function,

    SharePoint Framework - Call Azure Function

  12. Verify that for provided input, Azure function is returning expected output. Also, the status code is 200 OK.
  13. Click “Get function URL”.

    SharePoint Framework - Call Azure Function

  14. Copy the function URL.
Enable CORS on Azure Function
 
The Azure functions are hosted in MS Azure and they run in a different domain than our SharePoint site where our SharePoint Framework (SPFx) web part is hosted. By default, cross-domain calls are not allowed from SharePoint. To overcome this, we will have to enable CORS (Cross-Origin Resource Sharing) in Azure function.
 
Follow the below steps to enable CORS on Azure function,
  1. Click Platform features
  2. Under API, click CORS

    SharePoint Framework - Call Azure Function

  3. Specify the Office 365 tenant domain UEL and SharePoint local workbench URL.

    SharePoint Framework - Call Azure Function

  4.  Click Save
Develop SharePoint Framework Web Part
 
Open the command prompt. Create a directory for SPFx solution.
  1. md spfx-call-azure-function  
Navigate to the above-created directory.
  1. cd spfx-call-azure-function  
Run Yeoman SharePoint Generator to create the solution.
  1. yo @microsoft/sharepoint  
Yeoman generator will present you with the wizard by asking questions about the solution to be created.
 
SharePoint Framework - Call Azure Function
 
Solution Name: Hit Enter to have a default name (spfx-call-azure-function in this case) or type in any other name for your solution.
Selected choice: Hit enter
 
Target for the component: Here, we can select the target environment where we are planning to deploy the client webpart; i.e. SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice: SharePoint Online only (latest)
 
Place of files: We may choose to use the same folder or create a subfolder for our solution.
Selected choice: Same folder
 
Deployment option: Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selected choice: N (install on each site explicitly)
 
Type of client-side component to create: We can choose to create a client-side webpart or an extension. Choose the webpart option.
Selected choice: WebPart
 
Web part name: Hit enter to select the default name or type in any other name.
Selected choice: AzureFunctionWebPart
 
Web part description: Hit enter to select the default description or type in any other value.
Selected choice: Call Azure Function from SPFx
 
Framework to use: Select any JavaScript framework to develop the component. Available choices are (No JavaScript Framework, React, and Knockout)
 Selected choice: No JavaScript Framework
 
Yeoman generator will perform scaffolding process to generate the solution. The scaffolding process will take a significant amount of time.
 
Once the scaffolding process is completed, lock down the version of project dependencies by running the below command
  1. npm shrinkwrap  
In the command prompt type the below command to open the solution in the code editor of your choice.
  1. code .  

Code the webpart

  1. Open AzureFunctionWebPartWebPart.ts under “\src\webparts\azureFunctionWebPart\” folder
  2. Add the below imports
    1. import { HttpClient, SPHttpClient, HttpClientConfiguration, HttpClientResponse, ODataVersion, IHttpClientConfiguration, IHttpClientOptions, ISPHttpClientOptions } from '@microsoft/sp-http';  
  3. Implement method callAzureFunction as below,
    1. export default class AzureFunctionWebPartWebPart extends BaseClientSideWebPart<IAzureFunctionWebPartWebPartProps> {    
    2. protected functionUrl: string = "https://spfxcaller.azurewebsites.net/api/HttpTrigger";    
    3. protected callAzureFunction(): void {    
    4.       const requestHeaders: Headers = new Headers();    
    5.       requestHeaders.append("Content-type""text/plain");    
    6.       requestHeaders.append("Cache-Control""no-cache");    
    7.     
    8.       var siteUrl: string = this.context.pageContext.web.absoluteUrl;    
    9.       var userName: string = (<HTMLInputElement>document.getElementById("txtUserName")).value;    
    10.         console.log(`SiteUrl: '${siteUrl}', UserName: '${userName}'`);    
    11.         const postOptions: IHttpClientOptions = {    
    12.         headers: requestHeaders,    
    13.         body: `{ name: '${userName}' }`    
    14.       };    
    15.         let responseText: string = "";    
    16.       let resultMsg: HTMLElement = document.getElementById("responseContainer");    
    17.         this.context.httpClient.post(this.functionUrl, HttpClient.configurations.v1, postOptions).then((response: HttpClientResponse) => {    
    18.          response.json().then((responseJSON: IData) => {    
    19.             //responseText = JSON.stringify(responseJSON);    
    20.             if (response.ok) {    
    21.                 resultMsg.style.color = "white";    
    22.             } else {    
    23.                 resultMsg.style.color = "red";    
    24.             }    
    25.     
    26.             resultMsg.innerText = responseJSON.name;    
    27.           })    
    28.           .catch ((response: any) => {    
    29.             let errMsg: string = `WARNING - error when calling URL ${this.functionUrl}. Error = ${response.message}`;    
    30.             resultMsg.style.color = "red";    
    31.             console.log(errMsg);    
    32.             resultMsg.innerText = errMsg;    
    33.           });    
    34.       });    
    35.   }    
  4. Update render() method as below,
    1. public render(): void {    
    2.     this.domElement.innerHTML = `    
    3.       <div class="${ styles.azureFunctionWebPart }">    
    4.         <div class="${ styles.container }">    
    5.           <div class="${ styles.row }">    
    6.             <div class="${ styles.column }">    
    7.               <span class="${ styles.title }">Call Azure Function</span>    
    8.               <p class="${ styles.subTitle }">Customize SharePoint experiences using Web Parts.</p>    
    9.                   
    10.               <div class="${styles.row}">    
    11.                 <span class="ms-font-l ms-fontColor-white ${styles.label}">User name:</span>    
    12.                 <input type="text" id="txtUserName"></input>    
    13.               </div>    
    14.     
    15.               <button id="btnCallAzureFunction" class="${styles.button}">Say Hello!</button>    
    16.               <div id="responseContainer" class="${styles.label}"></div>    
    17.     
    18.             </div>    
    19.           </div>    
    20.         </div>    
    21.       </div>`;    
    22.     
    23.       document.getElementById("btnCallAzureFunction").onclick = this.callAzureFunction.bind(this);    
    24.   }    
Test the WebPart
  1. On the command prompt, type “gulp serve”
  2. Open SharePoint site
  3. Navigate to /_layouts/15/workbench.aspx
  4. Add the webpart to page.

    SharePoint Framework - Call Azure Function
Summary
 
Complex or long-running processing can be implemented outside of SharePoint in Azure functions and then can be easily consumed in SharePoint Framework client web parts.