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.

React FastAPI Python TypeScript Vite Syncfusion License


๐Ÿ“‘ 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.

Component Technology Purpose
๐ŸŽจ Frontend React 19 + Vite + Syncfusionยฎ EJ2 Render the interactive Pivot Table UI
โš™๏ธ Backend Python 3.11+ + FastAPI + Uvicorn Serve data, perform CRUD, return JSON responses
๐Ÿ”Œ Adaptor UrlAdaptor Bridge between Pivot Table and FastAPI REST endpoint
๐Ÿ“Š Sample Data In-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 / Package Version Purpose
๐Ÿ Python 3.11 or later Runtime for the FastAPI backend
๐Ÿ“ฆ venv Included with Python Creates an isolated Python environment for the backend
โšก FastAPI 0.110 or later REST API framework
๐Ÿš‚ Uvicorn 0.29 or later ASGI server for running the FastAPI application
๐ŸŸข Node.js 20.x LTS or later Runtime for the React dev server
๐Ÿ“ฆ npm / yarn / pnpm Latest stable Package manager
โš›๏ธ React 19.x or later Build the Pivot Table client
โšก Vite 8.1 or later React dev server and build tool
๐Ÿ“ฆ @syncfusion/ej2-react-pivotview 33.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 / Linux
source 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.

Field Data type Description
ProductID number Unique product identifier (primary key)
ProductName string Name of the product
Category string Category to which the product belongs
MRP number Maximum Retail Price of the product
Discount number Discount 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.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

# โœ… Import from routers folder
from routers.products import router as products_router

app = FastAPI(title="Products API")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# โœ… Register router
app.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 value Handler invoked
insert handle_insert()
update handle_update()
remove handle_remove()
(missing) Default read response
# filepath: FastAPIServer/routers/products.py
@router.post('/', response_class=JSONResponse)
async def list_or_crud(payload: Dict[str, Any]):
    action = payload.get('action')

    if action == 'insert':
        return handle_insert(payload, PRODUCTS, save_products, FIELDS_META)

    if action == 'update':
        return handle_update(payload, PRODUCTS, save_products)

    if action == 'remove':
        return handle_remove(payload, PRODUCTS, save_products)

    # Default read operation
    return JSONResponse({'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.py โ€“ handle_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.py โ€“ handle_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.py โ€“ handle_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.tsx
import * as React from 'react';
import { PivotViewComponent, CellEditSettings, Inject, FieldList } from '@syncfusion/ej2-react-pivotview';
import { DataManager, UrlAdaptor } from '@syncfusion/ej2-data';
import type { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import type { BeginDrillThroughEventArgs } from '@syncfusion/ej2-pivotview';
import './App.css';

function App(): React.ReactElement {
    // Configure DataManager with UrlAdaptor.
    const data: DataManager = new DataManager({
        url: 'http://localhost:8000/products/',
        adaptor: new UrlAdaptor(),
        crossDomain: true,
    });

    const dataSourceSettings: DataSourceSettingsModel = {
        dataSource: data,
        expandAll: true,
        rows: [{ name: 'ProductName' }],
        columns: [{ name: 'Category' }],
        values: [{ name: 'MRP' }],
        filters: [],
    };

    // Enable editing functionality
    const editSettings: 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'.
    };

    const pivotObj = React.useRef<PivotViewComponent>(null);

    // Configure beginDrillThrough event to set the primary key for CRUD operations
    function beginDrillThrough(args: BeginDrillThroughEventArgs) {
        // Iterate through all columns in the drill-through grid
        for (let i = 0; i < args.gridObj.columns.length; i++) {
            // Check if the current column is the primary key column
            if (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 records
                args.gridObj.columns[i].isPrimaryKey = true;
            }
        }
    }

    return (
        <div className='control-section' style={{ margin: 100 }}>
            <PivotViewComponent
                ref={pivotObj}
                id='PivotView'
                height={350}
                width={700}
                dataSourceSettings={dataSourceSettings}
                showFieldList={true}
                editSettings={editSettings}
                beginDrillThrough={beginDrillThrough}
            >
                <Inject services={[FieldList]} />
            </PivotViewComponent>
        </div>
    );
}

export default App;

๐Ÿ“ 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.

Step Action Expected 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 Table Pivot 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 Error The 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 data FastAPI 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 Found Updating 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 read A 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 saving The 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 restart Records 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 Table A 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 Blocked Console 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 mismatch Pivot 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 port The 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 packages The server fails to start with ModuleNotFoundError. Ensure your virtual environment is activated and pip install -r requirements.txt has been run.
๐Ÿ” Invalid JSON response Data 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.

Method Route Action payload Purpose Response
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 product The 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 key The 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:

Field Type Description
ProductID number Unique product identifier (primary key)
ProductName string Name of the product
Category string Category to which the product belongs
MRP number Maximum Retail Price of the product
Discount number Discount 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