docs/content/guides/getting-started/server-side-data/server-side-data-fetching.md
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.
[[toc]]
beforeDataProviderFetch — return false to cancel a fetch. The argument merges query fields with optional skipLoading (set on internal refetches after sort or CRUD). That flag is not passed to fetchRows.afterDataProviderFetch — result includes rows, totalRows, queryParameters, columnSortConfig, and filtersConditionsStack (the latter two mirror ColumnSorting and Filters state for the query that just ran).afterDataProviderFetchError — (error, queryParameters) when fetchRows throws or rejects with a non-abort error.afterDataProviderFetchAbort — (queryParameters, reason) when a fetch is superseded, aborted, or ends with AbortError.When notification is enabled, the Notification plugin shows an error toast 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 message 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. Fetch errors (including a failed refetch after a mutation) also add a Refetch button on the toast that calls hot.getPlugin('dataProvider').fetchData() again; the toast stays until you dismiss it or use Refetch. If Notification 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.
From hot.getPlugin('dataProvider'):
fetchData — refetch with optional overrides (page, pageSize, sort, filters, and client-only skipLoading). Overrides are merged into the current query; page is clamped to at least 1, and Handsontable may issue a follow-up fetch if totalRows from the server implies a lower last page than requested. After init, changing the dataProvider object through updateSettings runs the plugin’s update path and triggers a refetch when the grid is already rendered.updateSettings and loaded rows — When hasExternalDataSource is true, Handsontable only resets the in-memory placeholder to an empty array during init or when the updateSettings payload includes data or dataProvider. Other keys alone (for example height or colHeaders) do not clear the current page of rows or force a refetch; the DataProvider plugin refetches when its settings change. If you update columns without data or dataProvider, the data map is rebuilt but the same rule avoids wiping the grid with an empty dataset by accident.getQueryParameters — current page, 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).These end-to-end patterns pair a browser grid with a small backend:
GET / PATCH / POST / DELETE on /api/stock-lines). Reference server: server-rest.mjs (the same file ships in every data-provider example folder).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.
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.
VITE_API_BASE and VITE_GRAPHQL_URL.REACT_APP_API_BASE and REACT_APP_GRAPHQL_URL.VITE_API_BASE and VITE_GRAPHQL_URL.restApiBase and graphqlUrl in src/environments/environment.ts.Options
<div class="boxes-list"> </div>Plugins
<div class="boxes-list"> </div>Hooks
<div class="boxes-list"> </div>