Skip to content

Latest commit

ย 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Syncfusionยฎ React Pivot Table โ€“ Express.js Server Quick Start

A production-ready quick start that connects the Syncfusionยฎ React Pivot Table to an Express.js (Node.js + TypeScript) backend using the UrlAdaptor โ€” enabling remote data binding and full CRUD operations over REST endpoints.

React Express TypeScript Vite Syncfusion License


๐Ÿ“‘ Table of Contents


๐Ÿš€ Quick Overview

This project demonstrates how to bind the Syncfusionยฎ React Pivot Table to a remote Express.js backend using the UrlAdaptor of the DataManager. The UrlAdaptor provides full control over the request and response format, making it the perfect fit for any REST API โ€” including lightweight Node.js servers built with Express.

Component Technology Purpose
๐ŸŽจ Frontend React 19 + Vite + Syncfusionยฎ EJ2 Render the interactive Pivot Table UI
โš™๏ธ Backend Express.js 4.18 + TypeScript 5.3 Serve data, perform CRUD, return JSON responses
๐Ÿ”Œ Adaptor UrlAdaptor Bridge between Pivot Table and Express REST API
๐Ÿ“Š Sample Data In-memory productDetails list 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. Because every request is a POST, the same endpoint and code path can handle read, insert, update, and delete operations.


โœจ Key Features

  • ๐Ÿ“Š Remote Data Binding โ€“ Connects the Pivot Table to an Express.js REST endpoint over HTTP.
  • ๐Ÿ”„ Full CRUD Support โ€“ Insert, update, and delete records directly from the Pivot Table drill-through grid.
  • ๐ŸŽจ Type-Safe Backend โ€“ Built with TypeScript for compile-time safety on controllers, routes, and data models.
  • ๐Ÿ—‚๏ธ Standardized Response Format โ€“ Returns data as { result, count } when requiresCounts is true, or as a plain array otherwise.
  • ๐Ÿ”‘ Primary Key Configuration โ€“ Uses ProductID as the primary key for unique record identification.
  • ๐ŸŒ CORS-Enabled โ€“ Preconfigured with cors middleware 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 โ€“ Controllers wrap logic in try/catch and return meaningful HTTP status codes (422, 404, 500).
  • ๐Ÿ“ฆ 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
๐ŸŸข Node.js 20.x LTS or later Runtime for the Express.js server and React dev server
๐Ÿ“ฆ npm / yarn / pnpm Latest stable Package manager
โš›๏ธ React 19.x or later Build the Pivot Table client
๐ŸŸฃ TypeScript 5.3 or later Type-safe backend development
๐Ÿš‚ Express ^4.18.2 Framework for building the REST API
๐Ÿ”“ cors ^2.8.5 Enables CORS between the React client and Express
โšก Vite 8.1 or later React dev server and build tool
๐Ÿ“ฆ @syncfusion/ej2-react-pivotview 33.1.45+ React Pivot Table component
๐Ÿ“ฆ @syncfusion/ej2-data 33.1.45+ DataManager and UrlAdaptor

๐Ÿ“‚ Project Structure

syncfusion-react-pivot-with-express-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
โ”‚
โ”œโ”€โ”€ ๐Ÿ“ ExpressServer/                         # Express.js backend (Node.js + TypeScript)
โ”‚   โ”œโ”€โ”€ ๐Ÿ“ src/
โ”‚   โ”‚   โ”œโ”€โ”€ ๐Ÿ“ controllers/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ products.controller.ts        # CRUD handlers: get, create, update, delete
โ”‚   โ”‚   โ”œโ”€โ”€ ๐Ÿ“ routes/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ products.routes.ts            # POST routes for /, /create, /update, /remove
โ”‚   โ”‚   โ”œโ”€โ”€ ๐Ÿ“ types/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ interface.ts                  # ProductDetails & DataManagerRequest interfaces
โ”‚   โ”‚   โ””โ”€โ”€ ๐Ÿ“ utils/
โ”‚   โ”‚       โ””โ”€โ”€ data.ts                       # In-memory sample product data
โ”‚   โ”œโ”€โ”€ package.json                          # Express dependencies & scripts
โ”‚   โ”œโ”€โ”€ server.ts                             # Express app: CORS, JSON parsing, route mounting
โ”‚   โ””โ”€โ”€ tsconfig.json
โ”‚
โ”œโ”€โ”€ ๐Ÿ“„ README.md                              # You are here
 README reference

โš™๏ธ Installation & Setup

1. Clone the Repository

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

2. Backend โ€“ Express.js Server

The backend project lives in the ExpressServer/ folder.

2.1 Install npm dependencies

cd ExpressServer
npm install

2.2 Verify the dependencies

The package.json should include the following key packages:

{
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/cors": "^2.8.17",
    "@types/node": "^20.10.6",
    "typescript": "^5.3.3",
    "ts-node": "^10.9.2"
  }
}

Package descriptions:

  • express โ€“ Creates the Express.js server and handles REST API routes.
  • cors โ€“ Allows cross-origin requests from the React client to the Express server.
  • typescript โ€“ Adds TypeScript support to the backend.
  • ts-node โ€“ Runs TypeScript files directly without a separate build step.
  • @types/express, @types/cors, @types/node โ€“ Provide TypeScript type definitions.

Available scripts:

  • npm run dev โ€“ Starts the Express server in development mode using ts-node.
  • npm run build โ€“ Compiles TypeScript files into the dist/ folder.
  • npm start โ€“ Runs the compiled server (node dist/server.js).

2.3 Inspect the configuration

server.ts already configures:

  • โœ… CORS with origin: '*' for development (restrict this in production).
  • โœ… JSON body parsing for request payloads from the DataManager.
  • โœ… URL-encoded body parsing as a fallback.
  • โœ… Route mounting at /api/products.
// filepath: ExpressServer/server.ts
import express, { Application } from 'express';
import cors from 'cors';
import productsRoutes from './src/routes/products.routes';

const app: Application = express();
const PORT = process.env.PORT ? Number(process.env.PORT) : 5000;

// Enable CORS for all origins (configure as needed for production)
// The methods list reflects that the Syncfusion DataManager uses POST
// for all read and CRUD operations when paired with UrlAdaptor.
app.use(cors({
  origin: '*',
  methods: ['POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

// Parse JSON request bodies
app.use(express.json());

// Parse URL-encoded request bodies
app.use(express.urlencoded({ extended: true }));

// Mount product routes
app.use('/api/products', productsRoutes);

app.listen(PORT, () => {
  console.log(`Products endpoint: http://localhost:${PORT}/api/products`);
});

export default app;

๐Ÿ”’ Production CORS: Replace origin: '*' with the actual frontend domain, for example origin: 'https://yourdomain.com'.

2.4 Understand the data model

src/types/interface.ts defines the TypeScript contracts used throughout the backend:

// filepath: ExpressServer/src/types/interface.ts
export interface ProductDetails {
  ProductID?: number;
  ProductName?: string;
  Category?: string;
  MRP?: number;
  Discount?: number;
}

export interface DataManagerRequest {
  skip?: number;
  take?: number;
  requiresCounts?: boolean;
}

src/utils/data.ts exports an in-memory productDetails array used by the controllers.

2.5 Review the controllers

src/controllers/products.controller.ts exposes four handlers: getProducts, createProduct, updateProduct, and deleteProduct. Each handler:

  • Wraps the logic in a try/catch block.
  • Returns appropriate HTTP status codes (200, 201, 404, 422, 500).
  • Reads the payload from req.body.value || req.body so it works with both the UrlAdaptor's wrapped payload and direct POSTs.

2.6 Review the routes

src/routes/products.routes.ts mounts the controllers as POST endpoints:

// filepath: ExpressServer/src/routes/products.routes.ts
import { Router } from 'express';
import { getProducts, createProduct, updateProduct, deleteProduct } from '../controllers/products.controller';

const router = Router();

router.post('/', (req, res) => {
    return getProducts(req, res);
});
router.post('/create', (req, res) => { return createProduct(req, res); });
router.post('/update', (req, res) => { return updateProduct(req, res); });
router.post('/remove', (req, res) => { return deleteProduct(req, res); });

export default router;

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: 5000).

// 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 API_BASE_URL = 'http://localhost:5000/api/products';
    const data: DataManager = new DataManager({
        url: API_BASE_URL,
        insertUrl: API_BASE_URL + '/create',
        updateUrl: API_BASE_URL + '/update',
        removeUrl: API_BASE_URL + '/remove',
        adaptor: new UrlAdaptor()
    });

    const dataSourceSettings: DataSourceSettingsModel = {
        dataSource: data,
        expandAll: true,
        rows: [{ name: 'ProductID' }],
        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 (popup dialog) for 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 (var 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") {
                // 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 Express server runs on a different port, update API_BASE_URL accordingly. The same value is also used for insertUrl, updateUrl, and removeUrl.


โ–ถ๏ธ Running the Application

You need two terminals โ€” one for the backend API and one for the React client.

โ–ถ๏ธ Start the Backend (Terminal 1)

cd ExpressServer
npm run dev

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

Verify it works:

  • ๐ŸŒ Open http://localhost:5000/api/products in your browser or use a tool like Postman/curl.
  • โœ… You should see a JSON response containing the product records (array, or { result, count } if requiresCounts is true).

Sample request via curl:

curl -X POST http://localhost:5000/api/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": "USB Book Light", "Category": "Accessories", "MRP": 100.0, "Discount": 0.20 }
  ],
  "count": 25
}

๐Ÿ“ Note the port number in the terminal output and update API_BASE_URL in Client/src/App.tsx if it is different from 5000.

โ–ถ๏ธ 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 ProductID (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:5000/api/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 Network Request
1๏ธโƒฃ Double-click any pivot cell to open the drill-through grid showing underlying source records. Initial POST /api/products
โž• 2๏ธโƒฃ Click Add, fill in the new row fields, then click Update. POST http://localhost:5000/api/products/create
โœ๏ธ 3๏ธโƒฃ Click Edit on an existing row, change a field, then click Update. POST http://localhost:5000/api/products/update
๐Ÿ—‘๏ธ 4๏ธโƒฃ Click Delete on a row to remove it. POST http://localhost:5000/api/products/remove
๐Ÿ” 5๏ธโƒฃ The Pivot Table automatically refreshes to display the updated aggregated data from the backend. New POST /api/products

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


๐Ÿ”ง Troubleshooting

โ“ Issue ๐Ÿ” Symptom โœ… Resolution
๐Ÿšซ Empty Pivot Table Pivot loads with no errors but no rows or values appear. Ensure the API returns an array (or { result, count }) and that field names match the dataSourceSettings (case-sensitive).
404 Not Found Network tab shows a 404 response when the Pivot Table loads. Confirm the backend is running, the route prefix in server.ts is /api/products, and the url in App.tsx matches.
๐Ÿ’ฅ 500 Internal Server Error The Pivot Table fails and the browser shows a server error. Check the terminal output for stack traces. Common causes: a missing productDetails import or a malformed request body.
๐ŸŒ CORS Blocked Console shows Access to XMLHttpRequest ... has been blocked by CORS policy. Verify the cors middleware is registered in server.ts and that origin is set to a value that allows your dev server.
๐Ÿ’พ CRUD operations not saving The edit dialog closes but changes are not reflected in the data. Confirm the primary key is set in beginDrillThrough and that the CRUD routes (/create, /update, /remove) are mounted.
๐Ÿ”ค 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).
๐Ÿข Pivot Table loads slowly Rendering is sluggish or the browser hangs. Make sure requiresCounts is honored so the count field is returned and paging/virtual scrolling can be enabled.
๐Ÿ”Œ Wrong port The frontend cannot reach the backend. Confirm API_BASE_URL in Client/src/App.tsx matches the port the Express server is listening on (default 5000).
๐Ÿ› ๏ธ TypeScript compile errors npm run dev fails with TypeScript errors. Run npm install to ensure @types/express, @types/cors, and @types/node are installed, then retry.
๐Ÿ” Changes Not Reflected in Pivot Table A CRUD operation completes successfully, but the Pivot Table still shows the old data. Programmatically refresh the Pivot Table by calling pivotObj.current?.refresh(); after each operation if needed.

๐Ÿ“– API Reference

The backend exposes the following endpoints through productsRoutes. All endpoints accept POST requests because the Syncfusion DataManager with UrlAdaptor issues POST for read and CRUD operations.

Method Route Purpose Request Body Response
POST /api/products Retrieve product records (read) { skip?, take?, requiresCounts? } Array of products, or { result, count }
POST /api/products/create Insert a new product ProductDetails JSON (or { value }) 201 Created with the new product
POST /api/products/update Update an existing product (matched by ProductID) ProductDetails JSON (or { value }) 200 OK with the updated product
POST /api/products/remove Delete a product by primary key { key: ProductID } 200 OK with confirmation message

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 Express.js projects.
  • Keep changes focused โ€” one feature or fix per pull request.
  • Update or add documentation (README.md, express-js-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, Express.js, and TypeScript by the Syncfusionยฎ team.

About

This application demonstrates the integration of the Syncfusion React Pivot Table with an Express.js server for fetching and processing remote data.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages