---
name: devextreme-datasource
description: >
  Help developers configure the DevExtreme data layer: DataSource, ArrayStore, LocalStore,
  ODataStore, and CustomStore. Use when someone asks about binding components to data,
  loading data from a REST API, configuring an OData endpoint, implementing a custom data source,
  client-side vs server-side data processing, paging, sorting, filtering, or editing through a store.
  Trigger phrases: "DataSource", "ArrayStore", "ODataStore", "CustomStore", "LocalStore",
  "custom store", "load function", "byKey", "loadMode", "remoteOperations", "data binding",
  "server-side paging", "REST API data", "data layer".
compatibility: DevExtreme 26.1+. Framework-agnostic (TypeScript/JavaScript); UI snippets shown for Angular, React, Vue, and jQuery.
metadata:
  author: DevExpress
  version: "26.1"
---

# DevExtreme Data Layer Skill

A skill for configuring the DevExtreme data layer: `DataSource`, `ArrayStore`, `LocalStore`, `ODataStore`, and `CustomStore`.

## When to Use This Skill

- Binding a component (`dataSource` property) to a plain array, a URL, or a Store
- Configuring paging, sorting, filtering, and grouping at the DataSource level
- Connecting to an OData endpoint
- Implementing a `CustomStore` to load data from any REST API
- Implementing `insert`, `update`, `remove` in a `CustomStore` for editable components (DataGrid, etc.)
- Deciding between client-side and server-side data processing modes

## Before You Start

> ⚠️ **Always use DevExtreme's `CustomStore`, `DataSource`, or a built-in Store (`ArrayStore`, `ODataStore`) for data binding. Never replace them with raw `fetch`/`axios`, react-query, SWR, or any other data-fetching library. For user-visible error notifications, always use `notify` from `devextreme/ui/notify`.**

## Architecture Overview

```text
UI Component
    └── DataSource          — orchestrates paging, sorting, grouping, filtering
            └── Store       — handles raw data access (CRUD)
                    ├── ArrayStore   — in-memory array
                    ├── LocalStore   — HTML5 localStorage
                    ├── ODataStore   — OData v2/v3/v4 endpoint
                    └── CustomStore  — any backend (REST, GraphQL, etc.)
```

A component's `dataSource` property accepts:
- A **plain array** — DevExtreme wraps it in an `ArrayStore` + `DataSource` automatically.
- A **URL string** — DevExtreme fetches JSON and wraps it in a `DataSource`.
- A **Store instance** — passed directly or wrapped in a `DataSource`.
- A **DataSource instance** — gives full control over paging, sorting, grouping.
- A **DataSource config object** — DevExtreme constructs the `DataSource` for you.

## Documentation Reference Files

| File | When you need to |
|---|---|
| [references/stores.md](references/stores.md) | Use ArrayStore, LocalStore, or ODataStore |
| [references/custom-store.md](references/custom-store.md) | Implement CustomStore for any REST/custom backend |
| [references/datasource-options.md](references/datasource-options.md) | Configure DataSource-level options: paging, sort, filter, group, map, postProcess |

## Key API Summary

### DataSource options (most used)

| Option | Type | Description |
|---|---|---|
| `store` | `Store \| StoreConfig` | The underlying store |
| `filter` | `Filter` | Static client-side filter applied on load |
| `sort` | `Sort` | Default sort order |
| `group` | `Group` | Default grouping |
| `paginate` | `Boolean` | Enables paging (default `true` for most components) |
| `pageSize` | `Number` | Items per page (default `20`) |
| `requireTotalCount` | `Boolean` | Requests total item count alongside data |
| `reshapeOnPush` | `Boolean` | Triggers UI re-render on `push()` calls |
| `searchExpr` | `String \| Array` | Fields to search against |
| `searchValue` | `any` | Search value |
| `select` | `Array` | Field projection |
| `map` | `function(item)` | Transforms each item after load |
| `postProcess` | `function(data)` | Transforms the full loaded dataset |

### Store base options (all stores)

| Option | Description |
|---|---|
| `key` | Primary key field name(s) |
| `onLoaded` | Fires after data is loaded |
| `onInserting` / `onInserted` | Fires before/after insert |
| `onUpdating` / `onUpdated` | Fires before/after update |
| `onRemoving` / `onRemoved` | Fires before/after remove |
| `errorHandler` | Global error handler for store operations |

## Quick-Start Patterns

### Plain array (simplest)

```ts
// Any framework — just pass the array
dataSource = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
];
// In template / options: dataSource={dataSource} or [dataSource]="dataSource"
```

### ArrayStore with a DataSource

```ts
import ArrayStore from 'devextreme/data/array_store';
import DataSource from 'devextreme/data/data_source';

const store = new ArrayStore({
    key: 'id',
    data: [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' }
    ]
});

const dataSource = new DataSource({
    store,
    sort: 'name',
    pageSize: 10
});
```

### ODataStore

```ts
import ODataStore from 'devextreme/data/odata/store';

const dataSource = new ODataStore({
    url: 'https://services.odata.org/V4/Northwind/Northwind.svc/Products',
    key: 'ProductID',
    version: 4
});
```

### CustomStore (client-side mode — load all data at once)

```ts
import CustomStore from 'devextreme/data/custom_store';

const store = new CustomStore({
    key: 'ID',
    loadMode: 'raw', // DevExtreme handles paging/sorting/filtering client-side
    load() {
        return fetch('https://api.example.com/items').then(r => r.json());
    }
});
```

### CustomStore (server-side mode — component passes loadOptions)

See [references/custom-store.md](references/custom-store.md) for the full server-side pattern.

## Constraints & Rules

1. **`key` is critical**: Always set `key` on a Store when the component needs to identify rows (DataGrid, editing, selection). Without it, edits and selections may break silently.
2. **`loadMode: 'raw'` scope**: Use `raw` for List, SelectBox, TagBox, DropDownBox, and similar. DataGrid, TreeList, PivotGrid, and Scheduler default to processed mode — omit `loadMode` for those.
3. **No `DataSource` mutation after binding**: Do not swap `store` or `data` references on an existing `DataSource`. Call `dataSource.reload()` to refresh, or recreate the `DataSource`.
4. **Promise return**: All `CustomStore` functions (`load`, `byKey`, `insert`, `update`, `remove`) must return a `Promise`. Using jQuery's `$.Deferred` is acceptable in jQuery projects.
5. **`byKey` is required** when using components with value display (SelectBox, Lookup, AutoComplete, DropDownBox) with server-side stores — without it, selected value labels will not resolve.
6. **Error propagation**: Throw or reject with a message string inside store functions — DevExtreme surfaces it to the UI.
7. **Version consistency**: All `devextreme` and `devextreme-*` packages must be the same version.
8. **No fabricated API**: Never guess option names, `LoadOptions` fields, or store method signatures. Use the DxDocs MCP or official docs to verify if unsure.

## Using the DxDocs MCP

- **Search**: `mcp_dxdocs_devexpress_docs_search({ technology: "JavaScript", query: "..." })`
- **Fetch**: `mcp_dxdocs_devexpress_docs_get_content({ url: "..." })`

Use for: `LoadOptions` fields, `ODataContext`, filter expression syntax, `Query` API, `PivotGridDataSource`.

## Official Resources

- [Data Layer overview](https://js.devexpress.com/Documentation/Guide/Data_Binding/Data_Layer/)
- [DataSource API reference](https://js.devexpress.com/Documentation/ApiReference/Data_Layer/DataSource/)
- [CustomStore API reference](https://js.devexpress.com/Documentation/ApiReference/Data_Layer/CustomStore/)
- [ODataStore API reference](https://js.devexpress.com/Documentation/ApiReference/Data_Layer/ODataStore/)
- [ArrayStore API reference](https://js.devexpress.com/Documentation/ApiReference/Data_Layer/ArrayStore/)
- [Data Source Examples](https://js.devexpress.com/Documentation/Guide/Data_Binding/Data_Source_Examples/)
