RESTful API
@ginjou/with-rest-api is a fetcher adapter for generic REST-style backends.
It follows json-server-style query conventions such as _start, _end, _sort, and _order.
This package only provides a fetcher adapter. It does not provide auth, authz, realtime, notification, or i18n contracts.
Installation
pnpm add @ginjou/with-rest-api
yarn add @ginjou/with-rest-api
npm install @ginjou/with-rest-api
bun add @ginjou/with-rest-api
Scope
The adapter implements these fetcher methods today.
| Fetcher method | Status | Notes |
|---|---|---|
getList | Supported | Uses json-server-style pagination, sorter, and filter query keys. |
getOne | Supported | Requests {resource}/{id}. |
createOne | Supported | Sends params as the request body. |
updateOne | Supported | Sends params as the request body to {resource}/{id}. |
deleteOne | Supported | Sends params as the request body when present. |
custom | Supported | Powers both useCustom and useCustomMutation. |
getMany | Supported | Requests {resource}?id=1&id=2 in one call. |
createMany | Handled by core | Not implemented here. |
updateMany | Handled by core | Not implemented here. |
deleteMany | Handled by core | Not implemented here. |
json-server has no bulk endpoints, so the adapter does not implement the *Many mutations. useCreateMany, useUpdateMany, and useDeleteMany fall back to one createOne / updateOne / deleteOne request per record and return the responses in the same order as the input.
getMany is the one bulk read the backend can do in a single request. The backend decides the order of the returned records, and ids it does not know about are simply absent from the response, so data can be shorter than ids. Pass aggregate: false and read records by id if you need to match them up.
Fetcher
Use createFetcher() to build one Ginjou fetcher and register it through defineFetchersContext().
createFetcher() accepts these props.
| Prop | Required | Meaning |
|---|---|---|
url | Yes | Base URL used by the resource methods. |
client | No | Custom ofetch-compatible client. If omitted, the adapter creates its own client. |
In Vue, register it at the app root.
import { defineFetchersContext } from '@ginjou/vue'
import { createFetcher } from '@ginjou/with-rest-api'
defineFetchersContext({
default: createFetcher({
url: 'https://api.example.com',
}),
})
In Nuxt, pass $fetch when you want the adapter to use Nuxt's request client.
import { defineFetchersContext } from '@ginjou/vue'
import { createFetcher } from '@ginjou/with-rest-api'
export default () => defineFetchersContext({
default: createFetcher({
url: '/api',
client: $fetch as any,
}),
})
URLs
These are the request shapes produced by the adapter.
| Method | Default HTTP method | Path | Query | Body |
|---|---|---|---|---|
getList | GET | {url}/{resource} | pagination + sorters + filters | none |
getOne | GET | {url}/{resource}/{id} | none | none |
getMany | GET | {url}/{resource} | id repeated once per id | none |
createOne | POST | {url}/{resource} | none | params |
updateOne | PUT | {url}/{resource}/{id} | none | params |
deleteOne | DELETE | {url}/{resource}/{id} | none | optional params |
custom | normalized from top-level method | url as passed to the method | sorters + filters + query | payload is forwarded as request params |
Resource methods use url as baseURL and build the final path from the resource name.
custom is different. It uses the provided url directly instead of combining it with the fetcher base URL.
custom also forwards payload as request params, not as the request body.
Pagination
Pagination is translated into _start and _end.
| Ginjou input | REST query |
|---|---|
current | Used to calculate _start and _end |
perPage | Used to calculate _start and _end |
The adapter uses this formula:
| Query key | Value |
|---|---|
_start | (current - 1) * perPage |
_end | current * perPage |
For totals, the adapter reads the x-total-count response header.
If that header is missing, it falls back to data.length.
Filters
Only a small set of operator mappings is implemented.
| Ginjou filter | REST query key |
|---|---|
{ field: 'q', operator: 'eq', value } | q=value |
eq | field=value |
ne | field_ne=value |
gte | field_gte=value |
lte | field_lte=value |
contains | field_like=value |
Logical filters are not supported.
| Operator | Behavior |
|---|---|
or | Throws an error |
and | Throws an error |
Other operators do not currently get a dedicated suffix mapping in this adapter.
They fall back to a plain field=value query key.
Sorters
Sorters are translated into _sort and _order.
Multiple sorters are joined with commas.
| Ginjou input | REST query |
|---|---|
[{ field: 'name', order: 'asc' }] | _sort=name, _order=asc |
[{ field: 'name', order: 'asc' }, { field: 'age', order: 'desc' }] | _sort=name,age, _order=asc,desc |
Meta
For the standard resource methods, meta.method and meta.headers are forwarded into the outgoing request.
| Meta field | Used by |
|---|---|
meta.method | getList, getOne, getMany, createOne, updateOne, deleteOne |
meta.headers | getList, getOne, getMany, createOne, updateOne, deleteOne |
For custom, use the top-level method and headers props instead.
The adapter normalizes custom methods like this:
| Custom method input | HTTP method sent |
|---|---|
get and unknown strings | GET |
delete | DELETE |
post, put, patch | PUT |
This is the literal current implementation, so post and patch are also sent as PUT.
import { useGetList } from '@ginjou/vue'
useGetList({
resource: 'posts',
meta: {
method: 'POST',
headers: {
'X-Custom': 'value',
},
},
})