@aginix/vulcan-data-provider
v0.1.0
Published
React Admin data provider for APIs built with @aginix/adonis-vulcan
Maintainers
Readme
@aginix/vulcan-data-provider
React Admin data provider for APIs built
with @aginix/adonis-vulcan's BaseResourceController.
It speaks the same PostgREST-style filter grammar that BaseResourceController
parses on the server side (?status=eq.active&age=gte.18), so any controller
that extends BaseResourceController is wired up automatically.
Install
pnpm add @aginix/vulcan-data-provider react-adminreact-admin (or ra-core) is a peer dependency.
Usage
import { Admin, Resource, fetchUtils } from 'react-admin'
import { vulcanDataProvider } from '@aginix/vulcan-data-provider'
import { PostList, PostEdit, PostCreate } from './posts'
const dataProvider = vulcanDataProvider({
apiUrl: '/api',
httpClient: fetchUtils.fetchJson,
})
export const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate} />
</Admin>
)How it maps to the API
| react-admin call | HTTP method + URL |
| ------------------- | --------------------------------------------------- |
| getList | GET /<resource>?page=&perPage=&order=&<filters> |
| getOne | GET /<resource>/<id> |
| getMany | GET /<resource>?id=in.(...)&page=1&perPage=<n> |
| getManyReference | GET /<resource>?<target>=eq.<id>&... |
| create | POST /<resource> |
| update | PUT /<resource>/<id> (only changed fields) |
| updateMany | PUT /<resource>/<id> looped (no batch endpoint) |
| delete | DELETE /<resource>/<id> |
| deleteMany | DELETE /<resource>/<id> looped |
Response envelope (matches ResourceSerializer / Lucid pagination):
// list
{ "data": [...], "metadata": { "total": 99, "perPage": 25, "currentPage": 1, ... } }
// single
{ "data": { ... } }
// destroy → 204 No ContentFilter grammar
The data provider reads react-admin's filter object and emits
PostgREST-flavored query strings that @aginix/adonis-vulcan's FilterParser
understands.
| Filter shape | URL fragment |
| ------------------------------------------------- | ----------------------------------------- |
| { status: 'active' } | status=eq.active |
| { 'age@gt': 18 } | age=gt.18 |
| { 'name@like': 'foo bar' } | name=like.*foo*&name=like.*bar* |
| { tags: ['admin', 'user'] } | tags=in.(admin,user) |
| { q: 'search' } | q=search (no operator) |
| { faculty: { name: 'CS' } } | faculty.name=eq.CS (embedded filter) |
| { 'curriculums.exists': 'true' } | curriculums.exists=true (special key) |
Set defaultListOp on the config to change the implicit operator from eq to
something else (e.g. 'ilike' for free-text fields).
Compound primary keys
For resources whose primary key is more than one column, register the column list when constructing the provider:
import { vulcanDataProvider, defaultPrimaryKeys } from '@aginix/vulcan-data-provider'
defaultPrimaryKeys.set('enrollments', ['student_id', 'course_id'])
const dataProvider = vulcanDataProvider({
apiUrl: '/api',
httpClient: fetchUtils.fetchJson,
primaryKeys: defaultPrimaryKeys,
})The provider then synthesizes a virtual id (a JSON-encoded array of the key
parts) so react-admin's record-by-id flow keeps working transparently.
Configuration
type VulcanDataProviderConfig = {
apiUrl: string
httpClient: (url: string, options?: any) => Promise<{ status, headers, body, json }>
defaultListOp?: VulcanOperator // default: 'eq'
sortOrder?: VulcanSortOrder // default: ASC nullsLast / DESC nullsFirst
primaryKeys?: Map<string, readonly string[]> // default: ['id'] for every resource
pageParam?: string // default: 'page'
perPageParam?: string // default: 'perPage'
}