Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Syncfusion® React Pivot Table – FastAPI Server Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to a Python FastAPI backend using the UrlAdaptor — enabling remote data binding and full CRUD operations over REST endpoints.

ReactFastAPIPythonTypeScriptViteSyncfusionLicense


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote Python FastAPI backend using the UrlAdaptor of the DataManager. The UrlAdaptor issues POST requests to a single endpoint and routes them to create, read, update, or delete handlers based on an action field in the payload, making it a clean fit for lightweight Python REST services.

ComponentTechnologyPurpose
🎨 FrontendReact 19 + Vite + Syncfusion® EJ2Render the interactive Pivot Table UI
⚙️ BackendPython 3.11+ + FastAPI + UvicornServe data, perform CRUD, return JSON responses
🔌 AdaptorUrlAdaptorBridge between Pivot Table and FastAPI REST endpoint
📊 Sample DataIn-memory PRODUCTS list (from products_data.json)Simulate product sales records for the Pivot Table

💡 The UrlAdaptor is ideal when you want full server-side control over query processing, filtering, and data transformation. A single POST /products/ endpoint inspects the action property of the request payload (insert, update, remove, or none) and forwards it to the corresponding service handler.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to a FastAPI REST endpoint over HTTP.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • 🐍 Async API Backend – Built with FastAPI for high-performance, async REST endpoints with automatic documentation (Swagger UI at /docs).
  • 🗂️ Standardized Response Format – Returns data as { result, count }, which is what UrlAdaptor expects when requiresCounts is true.
  • 🔑 Primary Key Configuration – Uses ProductID as the primary key for unique record identification during update and delete.
  • 🌐 CORS-Enabled – Preconfigured with CORSMiddleware to allow cross-origin requests from the Vite dev server.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 🛡️ Robust Error Handling – Service handlers return meaningful HTTP status codes (400, 404, 500) and clear error messages.
  • 🧩 Modular Service Layout – Insert, update, and delete logic lives in separate files under routers/services/ for easier maintenance.
  • 📦 Ready-to-Run – Clone, install, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / PackageVersionPurpose
🐍 Python3.11 or laterRuntime for the FastAPI backend
📦 venvIncluded with PythonCreates an isolated Python environment for the backend
⚡ FastAPI0.110 or laterREST API framework
🚂 Uvicorn0.29 or laterASGI server for running the FastAPI application
🟢 Node.js20.x LTS or laterRuntime for the React dev server
📦 npm / yarn / pnpmLatest stablePackage manager
⚛️ React19.x or laterBuild the Pivot Table client
⚡ Vite8.1 or laterReact dev server and build tool
📦 @syncfusion/ej2-react-pivotview33.1.45+React Pivot Table component

📂 Project Structure

syncfusion-react-pivot-with-fastapi-server/
├── 📁 Client/ # React frontend (Pivot Table) — Vite + TypeScript
│ ├── 📁 public/
│ ├── 📁 src/
│ │ ├── App.css # Component styles
│ │ ├── App.tsx # Pivot Table with UrlAdaptor + CRUD configuration
│ │ ├── index.css
│ │ ├── main.tsx # React entry point
│ │ └── 📁 assets/
│ ├── index.html
│ ├── package.json # React dependencies & scripts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
│
├── 📁 FastAPIServer/ # Python backend (FastAPI + Uvicorn)
│ ├── 📁 routers/
│ │ ├── __init__.py
│ │ ├── products.py # Router: loads data, defines API endpoints, routes CRUD actions
│ │ └── 📁 services/
│ │ ├── __init__.py
│ │ ├── insert.py # handle_insert() – add a new product record
│ │ ├── update.py # handle_update() – modify an existing record
│ │ └── remove.py # handle_remove() – delete a record by ProductID
│ ├── main.py # FastAPI app: CORS, router registration (/products prefix)
│ ├── products_data.json # Sample product data source (16 records)
│ └── requirements.txt # Python dependencies (fastapi, uvicorn)
│
├── 📄 README.md # You are here
└── 📄 fastapi-server.md # UG documentation source for this sample

⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/syncfusion-react-pivot-with-fastapi-server.git
cd syncfusion-react-pivot-with-fastapi-server

2. Backend – FastAPI Server

The backend project lives in the FastAPIServer/ folder.

2.1 Create and activate a virtual environment

A virtual environment keeps the Python packages used by this backend separate from other projects on your machine.

cd FastAPIServer
python -m venv venv
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# macOS / Linuxsource venv/bin/activate

2.2 Install the Python dependencies

pip install -r requirements.txt

The requirements.txt file includes the following key packages:

fastapi
uvicorn[standard]

Package descriptions:

  • fastapi – Creates the FastAPI application and handles REST API routing.
  • uvicorn – ASGI server used to run the FastAPI application.

2.3 Understand the data source

products_data.json provides the in-memory data source for the Pivot Table. It contains product records with the following fields.

FieldData typeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

The first three records are shown below for brevity. The complete file contains 16 product records (identical ProductName values across four Category values, with incrementing MRP and Discount).

[
{
"ProductID": 10001,
"ProductName": "Smartwatch",
"Category": "Electronics",
"MRP": 100.0,
"Discount": 1.02
},
{
"ProductID": 10002,
"ProductName": "Smartwatch",
"Category": "Accessories",
"MRP": 110.0,
"Discount": 1.12
},
{
"ProductID": 10003,
"ProductName": "Smartwatch",
"Category": "Home Appliances",
"MRP": 120.0,
"Discount": 1.22
}
]

📝 The Discount field is included for completeness and can be used as an additional value field in the Pivot Table. The minimal report in this sample summarizes only the MRP field, so Discount does not appear in dataSourceSettings.

2.4 Inspect the application entry point

main.py configures the FastAPI application, CORS middleware, and router registration:

# filepath: FastAPIServer/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddleware# ✅ Import from routers folderfromrouters.productsimportrouterasproducts_routerapp=FastAPI(title="Products API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ✅ Register routerapp.include_router(
products_router,
prefix="/products",
tags=["products"]
)

🔒 Production CORS: Replace allow_origins=["*"] with the actual frontend domain, for example allow_origins=["https://yourdomain.com"].

2.5 Review the router

routers/products.py loads the product data into memory at startup and exposes the API endpoints. Field metadata (FIELDS_META) describes the field names used in the data source and is passed to the insert handler so missing fields can be defaulted to None.

A single POST /products/ endpoint inspects the action property of the request payload and routes the request to the corresponding CRUD service handler:

action valueHandler invoked
inserthandle_insert()
updatehandle_update()
removehandle_remove()
(missing)Default read response
# filepath: FastAPIServer/routers/products.py@router.post('/', response_class=JSONResponse)asyncdeflist_or_crud(payload: Dict[str, Any]):
action=payload.get('action')
ifaction=='insert':
returnhandle_insert(payload, PRODUCTS, save_products, FIELDS_META)
ifaction=='update':
returnhandle_update(payload, PRODUCTS, save_products)
ifaction=='remove':
returnhandle_remove(payload, PRODUCTS, save_products)
# Default read operationreturnJSONResponse({'result': PRODUCTS, 'count': len(PRODUCTS)})

A GET /products/ endpoint is also provided for manual verification in a browser or API testing tool.

2.6 Review the CRUD services

The CRUD logic is split across separate files under routers/services/ for easier maintenance.

insert.pyhandle_insert() reads the record from payload['value'] (or the payload itself), auto-generates a ProductID when it is not provided, ensures all schema fields exist on the record, appends it to the in-memory PRODUCTS list, and returns the new record.

update.pyhandle_update() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['value']['ProductID']), merges the existing record with the incoming values (preserving the key), and returns the updated record. Returns 400 if the key is missing and 404 if the record is not found.

remove.pyhandle_remove() locates the record by ProductID (read from payload['key'], payload['ProductID'], or payload['record_id']), removes it from the PRODUCTS list, and returns the deleted record. Returns 400 if the key is missing and 404 if the record is not found.

⚠️Persistence:save_products() is intentionally a no-op in the sample. Runtime CRUD changes are kept only in memory and are discarded when the server restarts (the original contents of products_data.json are reloaded on every server start via _load_products()). To persist changes, replace save_products() with logic that writes back to products_data.json or a database.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Install the Syncfusion Pivot Table package

npm install @syncfusion/ej2-react-pivotview @syncfusion/ej2-data

3.3 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port (default in this repo: 8000).

// filepath: Client/src/App.tsximport*asReactfrom'react';import{PivotViewComponent,CellEditSettings,Inject,FieldList}from'@syncfusion/ej2-react-pivotview';import{DataManager,UrlAdaptor}from'@syncfusion/ej2-data';importtype{DataSourceSettingsModel}from'@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';importtype{BeginDrillThroughEventArgs}from'@syncfusion/ej2-pivotview';import'./App.css';functionApp(): React.ReactElement{// Configure DataManager with UrlAdaptor.constdata: DataManager=newDataManager({url: 'http://localhost:8000/products/',adaptor: newUrlAdaptor(),crossDomain: true,});constdataSourceSettings: DataSourceSettingsModel={dataSource: data,expandAll: true,rows: [{name: 'ProductName'}],columns: [{name: 'Category'}],values: [{name: 'MRP'}],filters: [],};// Enable editing functionalityconsteditSettings: CellEditSettings={allowEditing: true,// Enables the Edit button and allows users to modify existing records.allowAdding: true,// Enables the Add button and allows users to create new records.allowDeleting: true,// Enables the Delete button and allows users to remove records.mode: 'Normal'// Uses Normal mode (inline editing); other options: 'Dialog', 'Batch', 'CommandColumn'.};constpivotObj=React.useRef<PivotViewComponent>(null);// Configure beginDrillThrough event to set the primary key for CRUD operationsfunctionbeginDrillThrough(args: BeginDrillThroughEventArgs){// Iterate through all columns in the drill-through gridfor(leti=0;i<args.gridObj.columns.length;i++){// Check if the current column is the primary key columnif(args.gridObj.columns[i].field==="ProductID"){args.gridObj.columns[i].visible=true;// Mark this column as the primary key// This tells DataManager to use this column's value to uniquely identify recordsargs.gridObj.columns[i].isPrimaryKey=true;}}}return(<divclassName='control-section'style={{margin: 100}}><PivotViewComponentref={pivotObj}id='PivotView'height={350}width={700}dataSourceSettings={dataSourceSettings}showFieldList={true}editSettings={editSettings}beginDrillThrough={beginDrillThrough}><Injectservices={[FieldList]}/></PivotViewComponent></div>);}exportdefaultApp;

📝 If your FastAPI server runs on a different port, update the url value in Client/src/App.tsx accordingly. The default in this repo is 8000.

Code explanation:

  • DataManager – Configured with the FastAPI endpoint at http://localhost:8000/products/ to retrieve product data.
  • UrlAdaptor – Sends POST requests to the configured endpoint and processes the JSON response returned by the FastAPI backend.
  • dataSourceSettings – Defines the Pivot Table report layout.
    • rows – Displays ProductName values as row headers.
    • columns – Displays Category values as column headers.
    • values – Summarizes the MRP field for each row and column combination.
  • editSettings – Enables add, edit, and delete operations on the drill-through grid.
  • beginDrillThrough – Marks the ProductID column as the primary key (isPrimaryKey = true) before the drill-through grid is displayed, so update and delete operations target the correct record.
  • FieldList – Displays the Field List and allows fields to be rearranged across rows, columns, values, and filters.

▶️ Running the Application

You need two terminals — one for the backend API and one for the React client.

▶️ Start the Backend (Terminal 1)

Make sure your virtual environment is activated (see step 2.1), then from the FastAPIServer folder run:

uvicorn main:app --reload --port 8000

The server will start and listen on http://localhost:8000 by default.

Verify it works:

  • 🌐 Open http://localhost:8000/products/ in your browser, or use a tool like Postman/curl.
  • 📖 Interactive API docs are available at http://localhost:8000/docs (Swagger UI provided by FastAPI).
  • ✅ You should see a JSON response containing the product records as { result, count }.

Sample request via curl:

curl -X POST http://localhost:8000/products/ \
-H "Content-Type: application/json" \
-d '{"requiresCounts": true, "skip": 0, "take": 10}'

Sample response:

{
"result": [
{ "ProductID": 10001, "ProductName": "Smartwatch", "Category": "Electronics", "MRP": 100.0, "Discount": 1.02 },
{ "ProductID": 10002, "ProductName": "Smartwatch", "Category": "Accessories", "MRP": 110.0, "Discount": 1.12 }
],
"count": 16
}

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it is different from 8000.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

The Vite dev server will start and display a URL (typically http://localhost:5173).

✅ Verify in the Browser

  1. Open the URL printed by Vite in your browser.
  2. You should see the Pivot Table populated with aggregated MRP values, grouped by ProductName (rows) and Category (columns).
  3. Open the browser's Developer Tools (F12) → Network tab.
  4. Reload the page.
  5. You should see a POST request to http://localhost:8000/products/ with status 200 and a JSON response containing the product records.
  6. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

StepActionExpected Action on Backend
1️⃣Double-click any pivot cell to open the drill-through grid showing underlying source records.Initial POST /products/ (read)
➕ 2️⃣Click Add, fill in the new row fields, then click Update.POST /products/ with action: "insert"
✏️ 3️⃣Click Edit on an existing row, change a field, then click Update.POST /products/ with action: "update"
🗑️ 4️⃣Click Delete on a row to remove it.POST /products/ with action: "remove"
🔁 5️⃣The Pivot Table automatically refreshes to display the updated aggregated data from the backend.New POST /products/ (read)

🔑 The ProductID column is automatically marked as the primary key inside the beginDrillThrough event, so update and delete operations know which record to target.

⚠️ Because save_products() is a no-op by design, any CRUD changes made at runtime are kept only in memory and are discarded when the server is restarted. This is expected behavior for the sample.


🔧 Troubleshooting

❓ Issue🔍 Symptom✅ Resolution
🚫 Empty Pivot TablePivot loads with no errors but no rows or values appear.Verify that the FastAPI endpoint returns data and that the response contains both the result and count properties. Ensure the field names returned by the backend match the fields configured in dataSourceSettings (case-sensitive).
🐍 500 Internal Server ErrorThe Pivot Table fails and the browser shows a server error.Check the server console for error messages. Verify that products_data.json exists, contains valid JSON, and can be read by the backend.
💥 500 on insert with empty dataFastAPI returns a 500 error when adding a record.handle_insert() computes the new ProductID with max([...]) + 1, which fails if the product list is empty. Ensure products_data.json is not empty.
404 Not FoundUpdating or deleting a record returns a 404 error.Verify that the ProductID sent in the request matches an existing record and that the record has not already been deleted.
🔄 CRUD operation ignored / falls back to readA record is added, updated, or deleted, but the backend always returns the full product list.Verify that the request payload includes the correct action value (insert, update, or remove). When action is missing or unrecognized, the router returns the default read response.
💾 CRUD operations not savingThe edit dialog closes but changes are not reflected in the data.Verify editing is enabled through editSettings and that ProductID is configured as the primary key in the beginDrillThrough event.
🧹 Changes lost after server restartRecords added, updated, or deleted earlier disappear when the FastAPI server is restarted.This is expected with the sample backend; save_products() is a no-op by design. To persist changes, implement file/database writes inside save_products().
🔄 Changes not reflected in Pivot TableA CRUD operation completes successfully, but the Pivot Table still shows the old data.Verify the backend processed the request successfully and returned updated data. Check the browser's Network tab for failed requests. If needed, call pivotObj.current?.refresh(); after an operation.
🌐 CORS BlockedConsole shows Access to XMLHttpRequest ... has been blocked by CORS policy.Verify CORSMiddleware is registered in main.py and that allow_origins permits your dev server's origin.
🔤 Property casing mismatchPivot appears empty or shows "field not found" even though the API returns data.Ensure field names in the API response match the Pivot Table's dataSourceSettings (e.g., ProductID, ProductName).
🔌 Wrong portThe frontend cannot reach the backend.Confirm the url in Client/src/App.tsx matches the port the FastAPI server is listening on (default 8000).
📦 Missing Python packagesThe server fails to start with ModuleNotFoundError.Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
🔁 Invalid JSON responseData cannot be loaded even though the request succeeds.Verify the backend returns a valid JSON response whose structure matches the expected { result, count } format.

If issues persist, use the browser's Developer Tools (F12) to inspect the Network and Console tabs.


📖 API Reference

The backend exposes endpoints through the products router. The Syncfusion DataManager with UrlAdaptor issues POST requests to the single /products/ endpoint; the action property in the request payload determines which operation is performed.

MethodRouteAction payloadPurposeResponse
GET/products/(none)Retrieve product records (manual verification){ result: [...], count: n }
POST/products/(no action)Retrieve product records (read from Pivot Table){ result: [...], count: n }
POST/products/{ "action": "insert", "value": { ... } }Insert a new productThe newly added product record
POST/products/{ "action": "update", "key": ProductID, "value": { ... } }Update an existing product (matched by ProductID)The updated product record
POST/products/{ "action": "remove", "key": ProductID }Delete a product by primary keyThe deleted product record

📖 Interactive Swagger UI documentation is available at http://localhost:8000/docs once the server is running.

The ProductDetails model exposes the following fields:

FieldTypeDescription
ProductIDnumberUnique product identifier (primary key)
ProductNamestringName of the product
CategorystringCategory to which the product belongs
MRPnumberMaximum Retail Price of the product
DiscountnumberDiscount value applied to the product

🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and FastAPI projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, fastapi-server.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, FastAPI, and Python by the Syncfusion® team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with a FastAPI server for fetching, processing, and serving remote data through REST APIs.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages