Server-side fetching and examples
Server-side fetching and examples
Hooks around fetchRows, loading and error UI, and the DataProvider plugin API. For CRUD callbacks, see Server-side CRUD. For query fields passed into fetchRows, see Server-side configuration.
Fetch hooks and loading UI
beforeDataProviderFetch— returnfalseto cancel a fetch. The argument merges query fields with optionalskipLoading(set on internal refetches after sort or CRUD). That flag is not passed tofetchRows.afterDataProviderFetch— result includesrows,totalRows,queryParameters,columnSortConfig, andfiltersConditionsStack(the latter two mirror ColumnSorting and Filters state for the query that just ran).afterDataProviderFetchError—(error, queryParameters)whenfetchRowsthrows or rejects with a non-abort error.afterDataProviderFetchAbort—(queryParameters, reason)when a fetch is superseded, aborted, or ends withAbortError.
When dialog is enabled, the Dialog plugin opens a modal if fetchRows rejects or if onRowsCreate, onRowsUpdate, or onRowsRemove rejects, including when a refetch after a successful mutation fails. The title is translated per operation (load vs create vs update vs remove). The description text prefers a string message, error, or detail from a JSON body, including when that body is nested on the error object (error.response?.data, error.data, or error.body, as with some HTTP clients). Otherwise it falls back to an Error message, a string rejection, or a generic fallback. If Dialog is disabled, use afterDataProviderFetchError for failed loads and refetches, and afterRowsMutationError for rejected mutation callbacks; you supply your own error UI.
The Empty data state / loading overlay follows DataProvider for the "loading" branch: beforeDataProviderFetch turns loading on when skipLoading is not set; afterDataProviderFetch and afterDataProviderFetchError turn it off. afterDataProviderFetchAbort does not clear loading by itself (for example when the user changes page while a request is in flight), so the overlay stays until a fetch finishes successfully or with an error. Refetches after column sort or CRUD pass skipLoading: true into beforeDataProviderFetch, so the Empty data state plugin skips the full loading overlay for those internal loads.
DataProvider plugin methods
From hot.getPlugin('dataProvider'):
fetchData— refetch with optional overrides (page,pageSize,sort,filters, and client-onlyskipLoading). Overrides are merged into the current query;pageis clamped to at least 1, and Handsontable may issue a follow-up fetch iftotalRowsfrom the server implies a lower last page than requested. After init, changing thedataProviderobject throughupdateSettingsruns the plugin’s update path and triggers a refetch when the grid is already rendered.updateSettingsand loaded rows — WhenhasExternalDataSourceistrue, Handsontable only resets the in-memory placeholder to an empty array during init or when theupdateSettingspayload includesdataordataProvider. Other keys alone (for exampleheightorcolHeaders) do not clear the current page of rows or force a refetch; the DataProvider plugin refetches when its settings change. If you updatecolumnswithoutdataordataProvider, the data map is rebuilt but the same rule avoids wiping the grid with an empty dataset by accident.getQueryParameters— currentpage,pageSize,sort,filters.getRowId— resolve the id for a visual row.createRows,updateRows,removeRows— programmatic CRUD through the same server callbacks (see Server-side CRUD).
Examples (REST and GraphQL)
These end-to-end patterns pair a browser grid with a small backend:
- REST (warehouse stock) — SKU / bin / quantity columns with an Express-style JSON API (
GET/PATCH/POST/DELETEon/api/stock-lines). Reference server:server-rest.mjs(the same file ships in everydata-providerexample folder). - GraphQL (support queue) — Paged open tickets, sort mapping, and mutations aligned to a helpdesk-style schema. Reference server:
server-graphql.mjs(same layout in each example folder).
The JavaScript, React, Vue 3, and Angular examples under examples/next/docs/ each run both backends on one page (default ports 4010 and 4011). See CodeSandbox examples below.
In TypeScript projects, you can import DataProviderQueryParameters, DataProviderFetchOptions, and related interfaces from handsontable/plugins/dataProvider (same pattern as the framework examples that share dataProviderClients with the REST and GraphQL clients).
When you enable filters, add the parameters your backend needs and serialize queryParameters.filters the way your storage layer expects. Handle errors with afterDataProviderFetchError and afterRowsMutationError.
CodeSandbox examples
Runnable projects under the Handsontable examples monorepo (Devbox):
End-to-end backends (Node servers + UI)
Each folder includes the same Express servers (server-rest.mjs, server-graphql.mjs, start-servers.mjs). Run npm run server, then npm run start for the framework dev server.
- JavaScript (Vite) — override URLs with
VITE_API_BASEandVITE_GRAPHQL_URL. - React (CRA) — override with
REACT_APP_API_BASEandREACT_APP_GRAPHQL_URL. - Vue 3 (Vite) — override with
VITE_API_BASEandVITE_GRAPHQL_URL. - Angular — set
restApiBaseandgraphqlUrlinsrc/environments/environment.ts.
More in this guide
- Server-side data
- Migrate from client-side data
- Configuration and query parameters
- Create, update, and remove
Related guides
Related API reference
- Option:
dataProvider - Plugin:
DataProvider - Hooks:
beforeDataProviderFetch,afterDataProviderFetch,afterDataProviderFetchError,afterDataProviderFetchAbort,hasExternalDataSource,modifyRowHeader(global row index with pagination),beforeRowsMutation,afterRowsMutation,afterRowsMutationError