Showing posts with label SPFx. Show all posts
Showing posts with label SPFx. Show all posts

January 29, 2026

Introducing Heft: The Modern Build Tool Replacing Gulp in SharePoint Framework (SPFx)

Introducing Heft: Modern Build Tool Replacing Gulp in SPFx Development

For a long time, Gulp was the default build tool for SharePoint Framework (SPFx) projects. Developers relied on familiar commands like gulp serve and gulp bundle to compile, package, and deploy their SPFx solutions.

However, as SPFx applications grew in size and complexity, the traditional Gulp-based build system began to struggle with performance, scalability, and long-term maintainability.

To address these challenges, Microsoft introduced Heft - a modern build orchestrator from the Rush Stack ecosystem - and made it the default SPFx build tool starting with SPFx v1.22.

In this article, we’ll explore the differences between SPFx Heft vs Gulp, why Microsoft made the switch, and how Heft improves the modern SharePoint Framework development workflow.

The Gulp Era in SharePoint Framework (SPFx)

In the early days, Gulp handled almost everything in an SPFx project:

  • Compiling TypeScript
  • Bundling with Webpack
  • Running the local dev server
  • Packaging .sppkg files
  • Automating the build pipeline

Typical workflows looked like this:

gulp serve
gulp bundle --ship
gulp package-solution --ship

For small projects, this worked fine. For large, long-living enterprise solutions, it did not.

Why Gulp Started to Fail

1. Slow Builds at Scale: Gulp runs tasks mostly sequentially, lacks smart caching, and often triggers full rebuilds for small changes. Result: Slow feedback loops and reduced productivity.

2. Fragile gulpfile.js: Task chains become complex, hard to debug, and frequently break during SPFx upgrades. Result: Build scripts harder to maintain than the app.

3. Poor Fit for Monorepos & Enterprise: Gulp wasn’t designed for monorepos, sharing build logic was painful, and dependency conflicts were common. Result: Scaling SPFx across teams became difficult.

4. Weak Type Safety & Debugging: Mostly JavaScript-based with unclear errors and poor traceability across tools. Result: Developers spent more time debugging the toolchain than writing features.

Enter Heft: The Modern SPFx Build Tool

Heft is a modern build orchestrator from Microsoft’s Rush Stack team, built to support large, enterprise-scale TypeScript solutions.

Unlike Gulp, which is a general-purpose task runner, Heft understands how modern development tools relate to one another - including TypeScript, ESLint, Jest, and Webpack.

Heft focuses on:

  • Clearly defined build phases
  • Plugin-based architecture
  • Incremental builds and smart caching
  • Parallel execution where possible

SPFx internally uses Heft to handle:

  • Compilation
  • Bundling
  • Linting
  • Testing
  • Packaging

SPFx Workflow Update: With SPFx v1.22, Gulp is replaced by Heft - but the developer experience remains familiar.

Task Command
Dev Server heft start
Production Build heft build --production
Package heft package-solution --production

These commands are mapped to standard npm scripts (npm start, npm run build), so day-to-day development workflows remain unchanged.

SPFx Heft vs Gulp: What Actually Changed?

Feature Gulp Heft
Build approach Scripted tasks Phase-based orchestration
Performance Slower at scale Faster with caching & parallelism
Configuration gulpfile.js JSON-based configs
Type safety Limited Strong
Monorepo support Weak Built-in
Debugging Hard to trace Clear errors & logs

Deployment: What Did NOT Change

The deployment process remains exactly the same:

  • Output is still a .sppkg file
  • Deployment still happens via:
  • SharePoint App Catalog
  • CI/CD pipelines (Azure DevOps, GitHub Actions)

Only the build engine changed - not the deployment process.

Node.js & SPFx Compatibility

  • SPFx v1.21.1+ → Node.js 22 LTS
  • Older SPFx → Node.js 16 / 18
  • SPFx ≤ 1.21 uses the Gulp-based toolchain
  • Heft becomes the default starting from SPFx 1.22

Heft officially replaces Gulp starting with SPFx 1.22 onward.

Why Heft Actually Matters

Moving to Heft brings real, practical benefits:

  • Faster rebuilds
  • Less configuration code
  • Fewer breaking changes
  • Consistent builds across teams

Less time fighting the build system, more time writing features.

Frequently Asked Questions (FAQs)

What is Heft in SharePoint Framework (SPFx)?

Heft is a modern build orchestrator developed by Microsoft’s Rush Stack team. It replaces the traditional Gulp-based build system in SharePoint Framework (SPFx) starting from version 1.22, providing faster builds, better scalability, and improved developer experience.

Why did Microsoft replace Gulp with Heft in SPFx?

Microsoft replaced Gulp with Heft to improve performance, maintainability, and scalability of SPFx projects. While Gulp worked well for smaller solutions, it struggled with large enterprise applications. Heft introduces incremental builds, parallel execution, and modern tooling integration.

Is Gulp still used in SPFx projects?

Yes, older SPFx versions (up to 1.21) still use the Gulp-based build system. Starting from SPFx version 1.22, Heft is the default build tool for all new and updated projects.

Does Heft change the SPFx deployment process?

No. The deployment process remains unchanged. Developers still generate .sppkg files and deploy them through the SharePoint App Catalog or automated CI/CD pipelines such as Azure DevOps and GitHub Actions.

Which Node.js version should be used with Heft in SPFx?

SPFx version 1.21.1 and later support Node.js 22 LTS, while older SPFx versions typically rely on Node.js 16 or 18 depending on compatibility.

Final Thoughts

Gulp served SPFx well in its early days, but modern enterprise needs demanded something better.

Heft is not just a replacement, it’s an upgrade.

The shift from Gulp to Heft reflects Microsoft’s move toward a faster, and more scalable build system for SharePoint Framework projects.

If you have any questions, reach out to our SharePoint Consulting team here.

June 5, 2025

Excel-Powered CRUD Operations in SharePoint List using PnP JS and Excel (XLSX) File

Overview

Utilizing Excel as an information source, PnP JS for SharePoint operations, and the XLSX library for parsing Exceed expectations records, I will walk you how to perform out CRUD (Create, Read, Update, Delete) method in a SharePoint list. 

We’ll cover two ways to handle the data: 

  • Static Approach – Hardcoded fields for quick implementation. 
  • Dynamic Approach – Automatically adapts to any list structure by reading Excel headers. 

Whether you're a beginner or a seasoned SharePoint developer, this guide will help you integrate Excel uploads with your SharePoint list seamlessly! 

CRUD operation using Excel in SharePoint via PnPJS and XLSX

Prerequisites

Sometime recently we start, guarantee you’ve got the desired bundles installed. Execute the following command in your terminal: 

npm install @pnp/sp @pnp/spfx @pnp/odata xlsx --save

Set Up Your SharePoint Framework (SPFx)

Configure the WebPart.ts File

import { SPFI, spfi } from "@pnp/sp";
import { SPFx } from "@pnp/sp";  
 
export let sp: SPFI;
 
protected async onInit(): Promise<void> {
 sp = spfi().using(SPFx(this.context));
 try {
   const message = await this._getEnvironmentMessage();
   this._environmentMessage = message;
   return super.onInit();
 } catch (error) {
   console.error("Error fetching environment message:", error);
 }
}

Create a Simple State Interface 

If you don't already have a state file, create one: 

export interface IExcelThroughUploadDataToSharePointState {
  fileName: string | null;
}

Handling Excel Upload in .tsx File

Import Required Modules

import * as XLSX from 'xlsx';
import { sp } from '../ExcelThroughUploadDataToSharePointWebPart';
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";  

Add Constructor

constructor(props: any) {
  super(props);
  this.state = {
    fileName: null
  };
}  

Handling File Upload and Data Processing

Upload & Process Excel File

private handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
  this.setState({ fileName: null });
  const file = event.target.files?.[0];
  if (!file) return;
  this.setState({ fileName: file.name });
 
  const reader = new FileReader();
  reader.onload = async (e: ProgressEvent<FileReader>) => {
    const data = e.target?.result;
    const workbook = XLSX.read(data, { type: 'binary' });
    const worksheet = workbook.Sheets[workbook.SheetNames[0]];
    const jsonData = XLSX.utils.sheet_to_json(worksheet) as any[];
    /*
      We are using as any[] with XLSX.utils.sheet_to_json(worksheet) to quickly
      convert the Excel sheet into JSON format without TypeScript errors or
      warnings. This is a fast and flexible mthod, easpecially useful during
      early development stages when typing isn't necessary. Alternatively,
      for better type safety and IntelliSense support, you can define
      a TypeScript interface like this:
      interfacee ITask {
        Title: string;
        Description: string;
        Status: string;
        DueDate: date;
      }
   
      Then use the generic version of sheet_to_json like this:
      const jsonData = XLSX.utils.sheet_to_json<ITask>(worksheet);
    */
 
    /* To fetch existing items from the SharePoint list named "ProjectTasks",
    we use the following PnPjs method: */
    const existingItems = await sp.web.lists.getByTitle("ProjectTasks").items
    .select("Id", "Title", "Description", "Status")();
    /*  
      In this code:
        - () is important - in the newer SPFI-based version of PnPjs
        (@pnp/sp version 2.x and above), instead of calling .get() at the
        end of the chain, you invoke the entire chain as a function using ().
      This returns the array of results directly.
    */

    /*  
      Create a map for easy lookup of Excel data.
      This helps in checking whether a specific item (based on Title)
      already exists.
        - If the item exists, it can be updated.
        - If it doesn't exist, it can be added.
        - If an existing item is no longer present in the Excel data,
        it can be removed.
    */
    const excelMap = new Map<string, any>();
    jsonData.forEach(item => {
      if (item.Title) excelMap.set(item.Title.trim(), item);
    });
 
    const spMap = new Map<string, any>();
    existingItems.forEach(item => {
      if (item.Title) spMap.set(item.Title.trim(), item);
    });
 
    /*  
      Perform update or create a new entry:
        - Check in the event that the thing from Excel exists within the SharePoint list.
      This ensures the Excel information, and the SharePoint list remains in at this point.
    */
    for (const [title, excelItem] of excelMap) {
      /* update items */
      if (spMap.has(title)) {
        const spItem = spMap.get(title);
        await await sp.web.lists.getByTitle('ProjectTasks').items
        .getById(spItem.Id).update({
             Title: excelItem.Title,
             Description: excelItem.Description,
             Status: excelItem.Status,
             DueDate: this.convertExcelSerialToDate(excelItem.DueDate).toISOString()
           });
        spMap.delete(title);
      } else {
        await sp.web.lists.getByTitle('ProjectTasks').items.add({
          Title: excelItem.Title,
          Description: excelItem.Description,
          Status: excelItem.Status,
          DueDate: this.convertExcelSerialToDate(excelItem.DueDate).toISOString()
        });
      }
    }
 
    /*
     Delete remaining SharePoint items not found in Excel  
    */
    for (const [title, spItem] of spMap) {
      await sp.web.lists.getByTitle("ProjectTasks").items
      .getById(spItem.Id).recycle(); // Use .delete() if permanent removal is needed
    }

    alert('Excel data synced with SharePoint list successfully!');
  };
  reader.readAsBinaryString(file);
}

Dynamic Approach

  /*  
    Dynamic field mapping:
      - This approach allows you to add or remove columns in the Excel file,
      and those changes will automatically reflect in the SharePoint List items
      without needing to change the code.
   */

  /* #### How it works: */
  const itemToAdd: any = {};
  Object.keys(excelItem).forEach(key => {
    itemToAdd[key] = excelItem[key];
  });

  /* #### add item  */
  await sp.web.lists.getByTitle("ProjectTasks").items.add(itemToAdd);

  /* #### update item */
  await sp.web.lists.getByTitle("ProjectTasks").items.getById(spItem.Id).update(itemToAdd);  

/*
  Explanation:
    - `Object.keys(excelItem)` retrieves all column names (as key) from the Excel row.
    - The loop dynamically builds a SharePoint item using those keys and values.
    - This ensures that any number of columns (2 or more) in Exceel will be correctly handled.
    - You do not need to hard-code field names - updates to Excel structure will still work.
*/

Converting Excel Date to SharePoint-Compatible Format

public convertExcelSerialToDate = (serial: number): Date => {
  return new Date((serial - 25569) * 86400 * 1000);
};

Explanation: 

Excel stores dates as serial numbers. This method converts it into a valid JavaScript Date object. 

/*  
  Convert Excel date to Unix Epoch (in milliseconds):
    - Excel stores dates as serial numbers, starting from Jan 1, 1970.
    - To convert Excel serial date to Unix epoch time (starts from Jan 1, 1970):
  Substract 25569 from the Excel serial date
    - because 25569 is the number of days between Jan 1, 1900 and Jan 1, 1970.
  Convert the result to seconds:
    - (serial - 25569) * 86400
    - 86400 is the number of seconds in a day.
  Convert to milliseconds:
    - seconds * 1000
    - Final result will be a JavaScript-compatible timestamp.
*/

Render Upload UI

public render(): React.ReactElement<IExcelThroughUploadDataToSharePointProps> {
  return (
    <div>
      <input type='file' accept=".xlsx, .xls" onChange={this.handleFileUpload} />
      {this.state.fileName && <p>Uploading: {this.state.fileName}</p>}
    </div>
  );
}

Tips for Using Excel Effectively

  • Ensure your Excel column headers match the internal field names in SharePoint. 
  • If you're using dates, convert them into text format in Excel before uploading. 
  • Dynamic field handling allows you to change list columns without editing the code. 

What You’ve Achieved

By the end of this tutorial, you now know how to: 

  • Upload an Excel file in SPFx. 
  • Parse the file using XLSX. 
  • Perform Create, Update, and Delete operations powerfully on a SharePoint list. 
  • Handle dates and dynamic columns gracefully. 
Excel through add items in SharePoint List

Final Thoughts

Using Excel as a front-facing CRUD interface makes SharePoint even more accessible for end-users. With PnP JS and XLSX to make the syncing handle makes it dynamic and future-proof!

If you have any questions you can reach out our SharePoint Consulting team here.

May 29, 2025

Streamline SPFx Builds with Azure DevOps CI/CD Pipeline – Part 1: Setting Up Continuous Integration

Introduction

If you're developing SharePoint Framework (SPFx) solutions, streamlining your build process is key. In this post, we’ll walk through setting up a Continuous Integration (CI) pipeline using Azure DevOps to automatically prepare your SPFx solution whenever you push code.


Step 1: Set Up the Repo

  • First, create a DevOps repository and add your SPFx source code into a dedicated folder.


Step 2: Create the CI Pipeline

  • Navigate to Pipelines: In Azure DevOps, go to the Pipelines section and click Create Pipeline.

  • Classic Editor: Choose the classic editor for easier configuration.

  • Repo & Branch: Select your repository and branch, then continue.

 

Step 3: Configure Pipeline Jobs

  • Empty Job: Choose the empty job template.
  • Add Tasks:

    1. Node.js Tool Installer : Set the Node.js version required for your SPFx project.
    2. NPM Install: Add the npm task to install dependencies (install command).
    3. Gulp Clean: Add the gulp task with clean.
    4. Gulp Build: Repeat the above for build.
    5. Gulp Bundle: Add another gulp task for bundle.
    6. Gulp Package Solution: Finally, a gulp task for package-solution

    Step 4: Handle Artifacts

    • Copy Files: Add a task to copy the generated .sppkg file from the solution folder to the drop folder.

    Step 5: Publish Artifacts

    Add a task to publish the pipeline artifacts for the next stage.

    Step 6: Set Up Triggers

    In the trigger settings, define the source code path. This ensures that every commit triggers the pipeline automatically.


    Conclusion:

    With this setup, every push to your repository will automatically trigger the CI pipeline, ensuring your SPFx solution is built and packaged consistently.

    Next Up: Part 2 – Deploy SPFx Solution to SharePoint App Catalog using Azure DevOps, we’ll cover how to automate the deployment of the .sppkg file to your SharePoint App Catalog as part of a Continuous Deployment (CD) pipeline.

    If you have any questions you can reach out our SharePoint Consulting team here.

    April 25, 2025

    Retrieving Viva Engage (Yammer) Posts in SPFx – A Complete Guide

    Overview

    With Microsoft Viva Engage (formerly Yammer) becoming a central hub for company-wide communication and community engagement, integrating its content directly into SharePoint through SPFx (SharePoint Framework) can provide a seamless user experience.

    While Viva Engage content can be embedded using the out-of-the-box (OOTB) web part, it may not provide the flexibility and customization required for more complex or tailored use cases. That’s where SPFx steps in. With custom development, you can fetch and display posts from specific communities, apply filters, or even perform additional business logic as needed.

    In this blog post, we will walk you through how to retrieve Viva Engage posts using SPFx and Microsoft Graph API. This is especially useful when the OOTB web part doesn't offer sufficient customization for your needs.

    Prerequisites

    Before diving into code, make sure you have the following ready:

    • SPFx development environment set up
    • Access to Microsoft 365 admin portal (to grant API permissions)
    • Viva Engage license and active community
    • Basic knowledge of React (optional, but helpful)

    Step 1: Register Your App in Azure AD

    To access Viva Engage data, you need permissions via Microsoft Graph API.

    1. Go to Azure Portal
    2. Navigate to Azure Active Directory > App registrations
    3. Click New registration
    4. Name your app (e.g., SPFxVivaEngageIntegration)
    5. Add redirect URI: https://localhost:5432
    6. Once registered, go to API permissions > Add permission
    7. Choose APIs my organization uses and search for Yammer
    8. Select Yammer and then Delegated permissions
    9. Add: user_impersonation
    10. Click Grant admin consent

    Step 2: Get Access Token

    Here’s how to get a token for Yammer (Viva Engage) using AAD token provider in SPFx:

     private async getViVaEngageToken(): Promise<void> {  
      try {  
       const tokenProvider = await this.props.spcontext.aadTokenProviderFactory.getTokenProvider();  
       const token = await tokenProvider.getToken("https://api.yammer.com");  
       this.setState(  
        { vivaEngageToken: token },  
        this.getAllPostsfromGroups  
       );  
      } catch (error) {  
       console.error("Error getting token: ", error);  
      }  
     }  
    

    Step 3: Retrieve Group ID Using Community Name

    This method fetches all groups and finds the group ID based on the community name.

     private async getGroupIdByName(communityName: string): Promise&lt;{ topLevelMessages: any[] }&gt; {  
      try {  
       const response = await this.props.spcontext.httpClient.get(  
        `https://api.yammer.com/api/v1/groups.json`,  
        HttpClient.configurations.v1,  
        {  
         headers: {  
          Authorization: `Bearer ${this.state.vivaEngageToken}`,  
          "Content-type": "application/json",  
         },  
        }  
       );  
       const data = await response.json();  
       const group = data.find((g: any) =&gt; g.full_name === communityName);  
       if (group) {  
        return await this.getPosts(group.id);  
       } else {  
        console.warn("Community not found.");  
        return { topLevelMessages: [] };  
       }  
      } catch (error) {  
       console.error("Error fetching group ID: ", error);  
       return { topLevelMessages: [] };  
      }  
     }  
    

    Step 4: Retrieve Posts from Viva Engage

    This method calls the messages API and returns only the top-level posts (ignores replies).

     private async getPosts(communityId: string): Promise&lt;{ topLevelMessages: any[] }&gt; {  
      try {  
       const apiUrl = `https://api.yammer.com/api/v1/messages/in_group/${communityId}.json?threaded=true`;  
       const response = await this.props.spcontext.httpClient.get(  
        apiUrl,  
        HttpClient.configurations.v1,  
        {  
         headers: {  
          Authorization: `Bearer ${this.state.vivaEngageToken}`,  
          "Content-type": "application/json",  
         },  
        }  
       );  
       const data = await response.json();  
       const messages = data?.messages || [];  
       const topLevelMessages = messages.filter((msg: any) =&gt; !msg.replied_to_id);  
       return { topLevelMessages };  
      } catch (error) {  
       console.error("Error fetching posts: ", error);  
       return { topLevelMessages: [] };  
      }  
     }  
    

    Conclusion

    By following the steps above, you can build a custom SPFx web part to pull Viva Engage posts using a community name and show them dynamically in your SharePoint site. This allows for a much more flexible and personalized experience beyond the OOTB web part.

    You now have a streamlined way to:

    • Authenticate with Azure AD
    • Retrieve a Viva Engage community’s group ID using its name
    • Fetch and return only the main posts (excluding replies)

    This approach is perfect if you're building SPFx web parts or extensions to display real-time conversations and announcements from Viva Engage.

    If you have any questions you can reach out our SharePoint Consulting team here.

    January 23, 2025

    Upload a Large File in SharePoint Document Library from SPFx Web Part

        Introduction:

        • In this blog, we will demonstrate how to upload large files to a SharePoint document library from the SPFx web part, even when the file size exceeds 100MB or even reaches the GB range.
        • SharePoint provides us with the REST API for uploading files to the document library. The API Is {site_url}/_api/web/getfolderbyserverrelativeurl('/sites/{site_name}/{library_name}')/file s/add(overwrite=true,url='{file_name}'). However, the issue is that this API only allows us to upload files up to 2MB in size. Any files larger than 2MB cannot be uploaded using this API. 
        •  Here I came up with a solution that allows us to upload files larger than 10 MB, going up to GBs, in SharePoint Document Library from the SPFx Web Part. To achieve this, we can use the chunk upload process. The SharePoint REST API provides methods Or query parameters for the chunk upload process, including “Start Upload”, “Continue Upload”, and “Finish Upload”. Using chunk upload, we can handle uploading any size of the file. 

        Function that handles the large upload in the SharePoint document library:

        • Create a custom function to handle large file uploads in the library. This function requires parameters such as file data, filename, SharePoint site URL, document library name, user's digest value, and desired chunk size.
        • At the beginning of the function, we need to declare some variables, such as the headers to be passed in our REST API, the starting point for uploading, and the endpoint, and so on.int, and so on.
        • After that, we need to call another function to started the upload session for file uploading. In this function, we simply add a blank file to our document library to initialize our uploading session. From the API response, we receive the unique ID of the blank file, which helps us identify the file whose content we need to overwrite.
        • After that, we need to generate a unique GUID that is used in our method for starting upload, continuing upload, and finishing upload.
        • After that, we have to check the starting position of the file. According to that, we divide the file into chunks. If the condition is true, then we have to call the "Start Upload" method with a unique GUID, which we have generated, to begin uploading the first chunk of the file to the document library.
        • After uploading the first chunk, we loop through every subsequent chunk of the file and call the "Continue Upload" REST API using the same GUID that we used in the "Start Upload" method to upload the chunks. We continue uploading the chunks until we reach the 2nd to last chunk.
        •  For the last step, we upload the last chunk of the file using the SharePoint REST API method called "Finish upload". to signal to SharePoint that this is the last chunk of the file, thus completing the uploading process.

        private async UploadLargeFile(
          file: Blob,
          siteUrl: string,
          libraryName: string,
          fileName: string,
          chunkSize: number,
          digest: any
        ) {
          const headers = {
            "Accept": "application/json;odata=verbose",
            "X-RequestDigest": digest
          };
          const fileSize = file.size;
          const uploadId = this.GenrateUploadId();
          let start = 0;
          let end = chunkSize;
          let chunkNumber = 0;
          let fileId = "";
        
          const uploadSessionResponse = await this.StartUploadSession(siteUrl, libraryName, fileName, headers);
          fileId = uploadSessionResponse.d.UniqueId;
        
          while (start < fileSize) {
            const chunk = file.slice(start, end);
            const isLastChunk = end >= fileSize;
        
            if (chunkNumber === 0) {
              await this.UploadFirstChunk(siteUrl, libraryName, fileName, chunk, uploadId, headers, fileId);
            } else if (isLastChunk) {
              await this.UploadLastChunk(siteUrl, libraryName, fileName, chunk, uploadId, headers, start, fileId);
            } else {
              await this.UploadIntermediateChunk(siteUrl, libraryName, fileName, chunk, uploadId, headers, start, fileId);
            }
        
            start = end;
            end = start + chunkSize;
            chunkNumber++;
          }
        }
        
        // Function for the generate unique GUI ID
        private GenrateUploadId(): string {
          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
            const r = Math.random() * 16 | 0;
            const v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
          });
        }
        
        // Starting Upload Session Method
        private async StartUploadSession(siteUrl: string, libraryName: string, fileName: string, headers: any) {
          try {
            return await this.Retry(async () => {
              const response = await fetch(
                `${siteUrl}/_api/Web/Lists/getByTitle('${libraryName}')/RootFolder/Files/Add(url='${fileName}',overwrite=true)`,
                {
                  method: 'POST',
                  headers: headers
                }
              );
        
              if (!response.ok) {
                const errorText = await response.text();
                console.error('Failed to start upload session:', errorText);
                throw new Error(`Failed to start upload session: ${errorText}`);
              }
        
              return response.json();
            });
          } catch (error) {
            console.error('Failed to start upload session after retries:', error);
            throw error;
          }
        }

        Start Upload Method:

        • This method is called when attempting to upload the first chunk of the file to our SharePoint document library.
        • In this method, we called the SharePoint Post REST API with the parameter of the start upload method along with a unique GUID.
        • The endpoint for the API is "`${siteUrl}/_api/web/GetFileById('${fileId}')/StartUpload(uploadId=guid'${uploadId}') `".
        private async UploadFirstChunk(
          siteUrl: string,
          libraryName: string,
          fileName: string,
          chunk: any,
          uploadId: string,
          headers: any,
          fileId: string
        ) {
          try {
            return await this.Retry(async () => {
              const response = await fetch(
                `${siteUrl}/_api/web/GetFileById('${fileId}')/StartUpload(uploadId=guid'${uploadId}')`,
                {
                  method: 'POST',
                  headers: headers,
                  body: chunk
                }
              );
        
              if (!response.ok) {
                const errorText = await response.text();
                console.error('Failed to upload first chunk:', errorText);
                throw new Error(`Failed to upload first chunk: ${errorText}`);
              }
        
              return response.json();
            });
          } catch (error) {
            console.error('Failed to upload first chunk after retries:', error);
            await this.CancelUpload(siteUrl, fileId, uploadId, headers);
            await this.DeleteFile(siteUrl, fileId, headers);
            throw error;
          }
        }


        Continue Upload Method:

        • The "Continue Upload" method in SharePoint's REST API allows for the upload of intermediate chunks of a file during a large file upload session.
        •   The API endpoint for continuing the upload is: "/_api/web/GetFileById('')/ContinueUpload(uploadId=guid'',fil eOffset=)". 
        • This endpoint specifies the file being uploaded (fileId), the unique upload session ID (uploadId), and the starting byte position of the chunk (fileOffset). 
        • The "file Offset" parameter specifies the starting byte position of the chunk being uploaded in the overall file. It helps SharePoint understand where this chunk fits within the entire file. 
        • it indicates the position in the file where the current chunk starts. 
        • For example, if the first chunk is 1MB (1048576 bytes) in size, the file Offset for the second chunk would be 1048576, the third chunk would be 2097152, and so on. 
        private async UploadIntermediateChunk(siteUrl: string, libraryName: string, fileName: string, chunk: any, uploadId: string, headers: any, start: any, fileId: string) {
            try {
              return await this.Retry(async () => {
                const response = await fetch(`${siteUrl}/_api/web/GetFileById('${fileId}')/ContinueUpload(uploadId=guid'${uploadId}',fileOffset=${start})`, {
                  method: 'POST',
                  headers: headers,
                  body: chunk
                });
        
                if (!response.ok) {
                  const errorText = await response.text();
                  console.error('Failed to upload chunk:', errorText);
                  throw new Error(`Failed to upload chunk: ${errorText}`);
                }
                return response.json();
              });
            } catch (error) {
              console.error('Failed to upload intermediate chunk after retries:', error);
              await this.CancelUpload(siteUrl, fileId, uploadId, headers);
              await this.DeleteFile(siteUrl, fileId, headers);
              throw error;
            }
          }

        Finish Upload Method:

        • The "Finish Upload" method is used to upload the final chunk of a large file to a SharePoint library, signaling the end of the upload process.
        • The method sends a POST request to the SharePoint API endpoint to finish the upload.
        • API endpoint is: "/_api/web/GetFileById('<fileId>')/FinishUpload(uploadId=guid'<uploadId>',fileOffset=<start>)".
        private async UploadLastChunk(siteUrl: string, libraryName: string, fileName: string, chunk: any, uploadId: string, headers: any, start: any, fileId: string) {
          try {
            return await this.Retry(async () => {
              const response = await fetch(`${siteUrl}/_api/web/GetFileById('${fileId}')/FinishUpload(uploadId=guid'${uploadId}',fileOffset=${start})`, {
                method: 'POST',
                headers: headers,
                body: chunk
              });
        
              if (!response.ok) {
                const errorText = await response.text();
                console.error('Failed to upload chunk:', errorText);
                throw new Error(`Failed to upload chunk: ${errorText}`);
              }
        
              return response.json();
            });
          } catch (error) {
            console.error('Failed to upload last chunk after retries:', error);
            await this.CancelUpload(siteUrl, fileId, uploadId, headers);
            await this.DeleteFile(siteUrl, fileId, headers);
            throw error;
          }
        }


        Cancel Upload And Delete File:

        • The "Cancel Upload" method is used to cancel an ongoing large file upload session in SharePoint. This is typically done when an error occurs during the upload process, and you want to terminate the session to prevent incomplete or corrupted files from being saved.
        • Sends a request to the SharePoint API to cancel the current upload session identified by uploadId.Utilizes the unique fileId and uploadId to specify which upload session to cancel.
        • Helps ensure that partially uploaded files are not left in an inconsistent state.
        • The "Delete File" method is used to delete a file from a SharePoint library. This is usually called after canceling an upload session to remove any partially uploaded files and clean up the SharePoint library.
        • Sends a request to the SharePoint API to delete the file identified by file.
        • Ensures that any incomplete or unwanted file uploads are removed, maintaining the integrity of the document library.
        private async CancelUpload(siteUrl: string, fileId: string, uploadId: string, headers: any) {
           try {
             const response = await fetch(`${siteUrl}/_api/web/GetFileById('${fileId}')/CancelUpload(uploadId=guid'${uploadId}')`, {
               method: 'POST',
               headers: headers
             });
        
             if (!response.ok) {
               const errorText = await response.text();
               console.error('Failed to cancel upload session:', errorText);
               throw new Error(`Failed to cancel upload session: ${errorText}`);
             }
        
           } catch (error) {
             console.error('Error occurred while canceling upload session:', error);
           }
         };
         private async DeleteFile(siteUrl: string, fileId: string, headers: any) {
           try {
             const response = await fetch(`${siteUrl}/_api/web/GetFileById('${fileId}')`, {
               method: 'DELETE',
               headers: headers
             });
        
             if (!response.ok) {
               const errorText = await response.text();
               console.error('Failed to delete file:', errorText);
               throw new Error(`Failed to delete file: ${errorText}`);
             }
           } catch (error) {
             console.error('Error occurred while deleting file:', error);
           }
         }


        Summary:

        This blog explains how to upload large files to SharePoint using a segmented approach for efficiency and reliability. It starts with the Start Upload Method, which initializes the upload session and prepares the file. Next, the Continue Upload Method handles middle segments, ensuring sequential upload using fileOffset. Finally, the Finish Upload Method completes the upload by sending the last segment, ensuring all parts are integrated into SharePoint. These methods include error handling and retries to ensure successful uploads, overcome file size limits,  and enhance system performance.

        If you have any questions you can reach out our SharePoint Consulting team here.