Devour JSON-API Client
Devour Client is a lightweight, framework-agnostic JavaScript client for consuming JSON:API compliant APIs. It simplifies the often painful process of manually serializing and deserializing JSON:API resources, providing clear conventions for pagination, filtering, sparse fields, and relationships. Currently stable at version 3.2.0, it focuses on offering a simple yet comprehensive feature set that differentiates it from other JavaScript JSON:API client implementations by abstracting away the complexities of the specification. While a specific release cadence isn't defined, the project sees regular maintenance and dependency updates. It provides a flexible middleware stack and configurable options for common API patterns like pluralization, trailing slashes, and authentication.
Common errors
-
Error: Resource 'comments' not defined in Devour's model directory.
cause Attempting to interact with a JSON:API resource type (e.g., 'comments') that has not been defined using `jsonApi.define('comments', { ... })` in your client initialization.fixDefine the missing resource model: `jsonApi.define('comments', { comment: '', post: { jsonApi: 'belongsTo', type: 'posts' } })` or ensure `disableErrorsForMissingResourceDefinitions` is set to `true` if you wish to suppress these errors. -
Failed to fetch
cause This generic network error usually indicates that the client could not connect to the `apiUrl` specified, either due to incorrect URL, CORS issues, network problems, or the API server being down.fixVerify the `apiUrl` is correct and accessible. Check your browser's console for CORS errors. Ensure your API server is running and reachable from the client's environment. -
Response code 401 (Unauthorized)
cause The API endpoint requires authentication, and the request was sent without proper authorization headers (e.g., bearer token, basic auth).fixPass the necessary authentication details to the `JsonApi` constructor: `new JsonApi({ apiUrl: '...', bearer: 'YOUR_TOKEN' })` or `new JsonApi({ apiUrl: '...', auth: { username: 'user', password: 'pass' } })`.
Warnings
- gotcha Enabling `disableErrorsForMissingResourceDefinitions` can mask issues where your API returns resources not explicitly defined in your client-side models, leading to unexpected data handling or UI errors.
- gotcha By default, Devour Client uses the `pluralize` package for converting singular model names (e.g., 'post') to plural API endpoints (e.g., '/posts'). Inconsistent pluralization between your client and API can lead to 404 errors. If your API uses different pluralization rules or singular resource paths, you must configure or disable this behavior.
- gotcha The `resetBuilderOnCall` option defaults to `true`, which clears the query builder stack after each `get`, `post`, `patch`, or `destroy` call. If you intend to chain multiple operations or reuse parts of a builder for subsequent requests without re-initializing, you might get unexpected results.
- breaking Devour Client's GitHub repository indicates 'Known Vulnerabilities' through Snyk. While specific CVEs are not detailed in the provided information, developers should be aware that dependencies or the client itself may have had or currently have security issues.
Install
-
npm install devour-client -
yarn add devour-client -
pnpm add devour-client
Imports
- JsonApi
const JsonApi = require('devour-client')import JsonApi from 'devour-client'
- JsonApi
const jsonApi = new JsonApi()
const jsonApi = new JsonApi({ apiUrl: 'http://your-api-here.com' })
Quickstart
import JsonApi from 'devour-client'
// Bootstrap the client with your API endpoint
const jsonApi = new JsonApi({apiUrl:'http://your-api-here.com'})
// Define a model schema matching your JSON:API resources
jsonApi.define('post', {
title: '',
content: '',
tags: [],
comments: {
jsonApi: 'hasMany',
type: 'comments'
}
})
jsonApi.define('comment', {
comment: '',
post: {
jsonApi: 'belongsTo',
type: 'posts'
}
})
async function runExample() {
try {
// Fetch all posts with pagination
const postsPage2 = await jsonApi.findAll('post', {page: {number: 2}});
console.log('Posts on page 2:', postsPage2.data.map(p => p.title));
// Fetch a single post by ID
const post = await jsonApi.find('post', '5');
console.log('Post 5:', post.data.title);
// Create a new post
const newPost = await jsonApi.create('post', {
title: 'Hello from Devour!',
content: 'This is some content for a new post.',
tags: ['devour', 'jsonapi']
});
console.log('Created post:', newPost.data.title);
// Update an existing post
const updatedPost = await jsonApi.update('post', {
id: newPost.data.id,
title: 'Updated title',
content: 'New content for the updated post',
tags: ['devour', 'update']
});
console.log('Updated post:', updatedPost.data.title);
// Delete a post
await jsonApi.destroy('post', updatedPost.data.id);
console.log('Deleted post:', updatedPost.data.id);
// Make an arbitrary request
const customRequest = await jsonApi.request('http://your-api-here.com/some-custom-endpoint', 'GET', { queryParam: 'value' });
console.log('Custom request result:', customRequest);
} catch (error) {
console.error('API Error:', error.errors || error.message);
}
}
runExample();