Gathering detailed insights and metrics for loopback-component-jsonapi
Gathering detailed insights and metrics for loopback-component-jsonapi
Gathering detailed insights and metrics for loopback-component-jsonapi
Gathering detailed insights and metrics for loopback-component-jsonapi
@mediasuite/loopback-component-jsonapi
JSONAPI support for loopback
jsonapi-serializer
A Node.js framework agnostic library for serializing your data to JSON API
@loopback/authentication
A LoopBack component for authentication support.
@loopback/core
Define and implement core constructs such as Application and Component
JSONAPI support for loopback.
npm install loopback-component-jsonapi
Typescript
Module System
Node Version
NPM Version
29.9
Supply Chain
84.4
Quality
68
Maintenance
25
Vulnerability
95.9
License
Updated on 05 Sept 2023
Minified
Minified + Gzipped
JavaScript (100%)
Cumulative downloads
Total Downloads
Last day
-66.7%
Compared to previous day
Last week
-21.2%
Compared to previous week
Last month
130.5%
Compared to previous month
Last year
-8.1%
Compared to previous year
9
jsonapi.org support for loopback.
This project is now pretty stable and is used in production in a number of our projects. There are known issues (see below and the issue tracker) these can mostly be worked around or are pretty minor. Open an issue on the issue tracker if you need clarification on anything or need help.
This module doesn't do complex compound documents very well yet. This means that if you try to do complex includes in a single request you will likely run into trouble.
We wrote another module called loopback-jsonapi-model-serializer
that does JSONAPI serialization very well (but nothing else) for loopback which you can use to get
around such issues for now. The long term goal is to swap out the serialization layer in
loopback-component-jsonapi
with loopback-jsonapi-model-serializer
We have created a sample project using EmberJS, Loopback and this compoment. It's called emberloop.
We are VERY interested in help. Get in touch via the issue tracker Please read the following about contributing:
This project uses Semantic Release to manage the release process.
This means that:
A. There is no semver project version in package.json
. This is managed in CI.
B. Commit messages need to follow conventions. See here for commit message guidelines.
The important things to remember are:
A. If you are fixing a bug prefix your commit message with fix(<thing being fixed goes here>):
B. If you are adding a non breaking feature, prefix your commit with feat(<name of feature goes here>):
C. If you are making a breaking change of any kind, prefix additional information on the 3rd line of the commit message with: BREAKING CHANGE:
See examples of this on the Semantic Release github pages.
And don't hesitate to reach out on our issue tracker
if you want further clarification.
This project is follows the Standard js styleguide. Linting happens on CI and any time you run tests via npm test
You can run the linting on its own with npm run lint
Additionally, code formatting is done whenever you run git commit. This is made possibly by lint-staged and husky with actual formatting done by prettier
All code is reviewed by one or more of the project maintainers before merging. Before becoming a maintainer, contributers need to fork the master branch of this repo, make their changes and submit a pull request.
Once a contributor becomes a maintainer, it is preferred that they create new branches on the loopback-component-jsonapi repo and submit those as pull requests
We take testing seriously. The project contains over 200 tests at time of writing this. In most cases we wont merge anything without tests. (Within reason of course)
We follow the principle of "Open open source" which means if you contribute even a single PR to the project, we make you a project maintainer.
You can enable debug logging by setting an environment variable:
DEBUG=loopback-component-jsonapi
DEBUG=loopback-component-jsonapi node .
In your loopback project:
npm install --save loopback-component-jsonapi
component-config.json
file in your server folder (if you don't already have one)component-config.json
1{ 2 "loopback-component-jsonapi": {} 3}
We are aiming to make the component as configurable as possible. You can configure how the component behaves with the options shown and listed below. If there is something else you would like to see be configurable, please submit an issue on the repository. For remote methods, root
must be set to true
.
Example: (all configuration options listed)
1{ 2 "loopback-component-jsonapi": { 3 "restApiRoot": "/api", 4 "host": "https://www.mydomain.com", 5 "enable": true, 6 "handleErrors": true, 7 "errorStackInResponse": false, 8 "handleCustomRemoteMethods": false, 9 "exclude": [ 10 {"model": "comment"}, 11 {"methods": "find"}, 12 {"model": "post", "methods": "find"}, 13 {"model": "person", "methods": ["find", "create"]} 14 ], 15 "hideIrrelevantMethods": true, 16 "attributes": { 17 "posts": ["title"] 18 }, 19 "include": [ 20 {"methods": "customMethod"}, 21 {"model": "post", "methods": "customMethod"}, 22 {"model": "person", "methods": ["customMethod1", "customMethod2"]} 23 ] 24 } 25}
Url prefix to be used in conjunction with host and resource paths. eg. http://127.0.0.1:3214/api/people
1{ 2 ... 3 "restApiRoot": "/api", 4 ... 5}
string
/api
The url of the application, to be used when constructing links to relationships. Useful where the service is proxied and the application believes it is running on a different url to that seen by the consuming service.
1{ 2 ... 3 "host": "https://www.mydomain.com", 4 ... 5}
string
null
Whether the component should be enabled or disabled. Defaults to true
, flip it to false
if you need to turn the component off without removing the configuration for some reason.
1{ 2 ... 3 "enable": true, 4 ... 5}
boolean
true
When true, the component will unregister all other error handling and register a custom error handler which always returns errors in JSON API compliant format. Validation errors include the correct properties in order to work out of the box with EmberJS.
1{ 2 ... 3 "handleErrors": true, 4 ... 5}
boolean
true
Along handleErrors, When true, this option will send the error stack if available within the error
response. It will be stored under the source.stack
key.
Please be careful, this option should never be enabled in a production environment. Doing so can expose sensitive data.
1{ 2 ... 3 "errorStackInResponse": NODE_ENV === 'development', 4 ... 5}
boolean
false
Allow all (custom) remote methods to be serialized by default.
This option can be overridden in any of the following ways:
1{ 2 ... 3 "handleCustomRemoteMethods": true, 4 ... 5}
boolean
false
Allows blacklisting of models and methods. Define an array of blacklist objects. Blacklist objects can contain "model" key "methods" key or both. If just "model" is defined then all methods for the specified model will not be serialized of deserialized using JSON API. If just the "methods" key is defined then all methods specified on all models will not be serialized or deserialized using JSON API. If a combination of "model" and "methods" keys are used then the specific combination of model and methods specified will not be serialized or deserialized using JSON API.
1{ 2 ... 3 "exclude": [ 4 {"model": "comment"}, 5 {"methods": "find"}, 6 {"model": "post", "methods": "find"}, 7 {"model": "person", "methods": ["find", "create"]} 8 ], 9 ... 10}
array
null
The default behavior is to modify (serialize to JSON API) the output of the following CRUD methods on all models:
find
create
updateAttributes
deleteById
findById
In addition the following wild card method names are matched and the output is modified in order to handle relationships eg. /api/posts/1/comments
__get__.*
__findRelationships__.*
The default behavior is to modify (deserialize from JSON API) the input to the following CRUD methods on all models:
create
updateAttributes
Allows whitelisting of methods. Define an array of whitelist objects. Whitelist objects can contain a "methods" key or both a "models" key and a "methods" key. If just the "methods" key is defined then the methods specified will be serialized or deserialized using JSON API on all models that have the specified methods. If a combination of "model" and "methods" keys are used then the specific combination of model and methods specified will be serialized or deserialized using JSON API.
Note: objects returned from a remote method that will be JSON API serialized MUST include an id property. id property can be null.
1{ 2 ... 3 "include": [ 4 {"methods": "customMethod"}, 5 {"model": "post", "methods": "customMethod"}, 6 {"model": "person", "methods": ["customMethod1", "customMethod2"]} 7 ], 8 ... 9}
array
null
By default, loopback-component-jsonapi
disables a number of methods from each endpoint
that are not JSON API relevant. These methods are:
upsert
exists
findOne
count
createChangeStream
updateAll
You can use this option to prevent loopback-component-jsonapi
from doing so. These methods are not modified by the component. Their output
will not be in a JSON API compliant format.
1{ 2 ... 3 "hideIrrelevantMethods": true, 4 ... 5}
boolean
true
By default, model properties will be converted to attributes in JSON API terms. All model properties except the primary key and any foreign keys will be copied into the attributes object before output. If you wish to limit which properties will be output as attributes you can specify a whitelist of attributes for each type.
1{ 2 ... 3 "attributes": { 4 "posts": ["title", "content"], 5 "comments": ["createdAt", "updatedAt", "comment"] 6 } 7 ... 8}
object
null
The attributes arrays are keyed by type not by model name. Type is the term used by JSON API to describe the resource type in question and while not required by JSON API it is usually plural. In loopback-component-jsonapi
it is whatever the models plural
is set to in model.json
. So in our example above we defined: "posts": ["title", "content"]
as the resource type for the post
model is posts
Allows configuration of whether the component should expose foreign keys (which the jsonapi spec considers implementation details) from the attributes hash.
Always expose foreign keys for all models
1{ 2 ... 3 foreignKeys: true, 4 ... 5}
Never expose foreign keys for any models (default behaviour)
1{ 2 ... 3 foreignKeys: false, 4 ... 5}
Only expose foreign keys for the comment model
1{ 2 ... 3 foreignKeys: [ 4 {model: 'comment'} 5 ], 6 ... 7}
Only expose foreign keys for the comment model findById method. eg. GET /api/comments/1
1{ 2 ... 3 foreignKeys: [ 4 {model: 'comment', method: 'findById'} 5 ], 6 ... 7}
boolean|array
false
jsonapi
remote method optionsSometimes you need to be able to control when a custom remote method should be handled by the component. By default, loopback-component-jsonapi
will not handle (serialize or deserialize) custom remote methods. In order to tell the component to handle a custom remote method, you have the following options (In priority order):
jsonapi
to true
when defining a custom remote method.exclude
array setting. (see above)include
array setting. (see above)handleCustomRemoteMethods
to true
in the component's settings. (see above)This option takes precedence and sets the component to handle or not handle the custom remote method.
1Post.remoteMethod('greet', { 2 jsonapi: true 3 returns: { root: true } 4})
Ensures that the response from Post.greet will follow JSONApi format.
1Post.remoteMethod('greet', {
2 jsonapi: false
3 returns: { arg: 'greeting', type: 'string' }
4})
Ensures that the response from Post.greet will never follow JSONApi format.
You must always pass root: true
to the returns
object when using loopback-component-jsonapi
. This is especialy important when you expect the response to be an array.
When loopback-component-jsonapi
serializes a custom remote method, by default it will assume that the data being serialized is of the same type as the model the custom remote method is being defined on. Eg. For a remote method on a Comment
model, it will be assumed that the data being returned from the remote method will be a comment or an array of comments. When this is not the case, you will need to set the type property in the returns
object in the remote method definition.
If an unknown type or no type are given, the model name will be used.
1Post.remoteMethod('prototype.ownComments', {
2 jsonapi: true
3 returns: { root: true, type: 'comment' }
4})
For occasions where you need greater control over the serialization process, you can implement a custom serialization function for each model as needed. This function will be used instead of the regular serialization process.
1module.exports = function (MyModel) {
2 MyModel.jsonApiSerialize = function (options, callback) {
3 // either return an error
4 var err = new Error('Unable to serialize record');
5 err.status = 500;
6 cb(err)
7
8 // or return serialized records
9 if (Array.isArray(options.records)) {
10 // serialize an array of records
11 } else {
12 // serialize a single record
13 }
14 cb(null, options);
15 }
16}
options
All config options set for the serialization process.callback
Callback to call with error or serialized recordsFor occasions where you need greater control over the deserialization process, you can implement a custom deserialization function for each model as needed. This function will be used instead of the regular deserialization process.
1module.exports = function (MyModel) { 2 MyModel.jsonApiDeserialize = function (options, callback) { 3 // either return an error 4 var err = new Error('Unable to deserialize record'); 5 err.status = 500; 6 cb(err) 7 8 // or 9 // options.data is the raw data 10 // options.result needs to be populated with deserialization result 11 options.result = options.data.data.attributes; 12 13 cb(null, options); 14 } 15}
Generic errors respond with a 500, but sometimes you want to have a better control over the error that is returned to the client, taking advantages of fields provided by JSONApi.
It is recommended that you extend the base Error constructor before throwing errors. Eg. BadRequestError
meta
and source
fields needs to be objects.
1module.exports = function (MyModel) { 2 MyModel.find = function () { 3 var err = new Error('April 1st, 1998'); 4 5 err.status = 418; 6 err.name = 'I\'m a teapot'; 7 err.source = { model: 'Post', method: 'find' }; 8 err.detail = 'April 1st, 1998'; 9 err.code = 'i\'m a teapot'; 10 err.meta = { rfc: 'RFC2324' }; 11 12 throw err 13 } 14} 15 16// This will be returned as : 17// { 18// errors: [ 19// { 20// status: 418, 21// meta: { rfc: 'RFC2324' }, 22// code: 'i\'m a teapot', 23// detail: 'April 1st, 1998', 24// title: 'I\'m a teapot', 25// source: { model: 'Post', method: 'find' } 26// } 27// ] 28// }
options
All config options set for the deserialization process.callback
Callback to call with error or serialized recordsoptions.type
Resource type. Originally calculated from a models plural. Is used in the default serialization process to set the type property for each model in a JSON API response.
posts
options.method
The method that was called to get the data for the current request. This is not used in the serialization process but is provided for custom hook and serialization context.
create
, updateAttributes
options.primaryKeyField
The name of the property that is the primary key for the model. This is usually just
id
unless defined differently in a model.json file.
options.requestedIncludes
The relationships that the user has requested be side loaded with the request.
For example, for the request GET /api/posts?include=comments
options.requestedIncludes
would be 'comments'
.
string
or array
'comments'
or ['posts', 'comments']
options.host
The host part of the url including any port information.
http://localhost:3000
options.restApiRoot
The api prefix used before resource information. Can be used in conjunction with
options.host
and options.type
to build up the full url for a resource.
/api
options.topLevelLinks
Links object used at the top level of the JSON API response structure.
{links: {self: 'http://localhost:3000/api/posts'}}
options.dataLinks
Links object used to generate links for individual resource items. The structure is
and object with JSON API link keys such as self
or related
that are defined as
a function that will be called for each resource.
Eg.
1options.dataLinks: { 2 self: function (resource) { 3 return 'http://localhost:3000/posts/' + resource.id; 4 } 5}
As shown above, each resource gets passed to the function and the result of the function is assigned to the key in the final JSON API response.
options.relationships
This contains all the relationship definitions for the model being serialized.
Relationship definition objects are in the same format as in loopback's Model.relations
definition. An object with relationship name keys, each having properties:
modelTo
loopback model objectkeyTo
name of key on to modelmodelFrom
loopback model objectkeyFrom
name of key on from modeltype
type of relationship (belongsTo, hasOne, hasMany)This information is used to build relationship urls and even setup side-loaded data correctly during the serialization process.
eg.
1options.relationships = { 2 comments: { modelTo: ...etc }, 3 tags: { modelTo: ...etc } 4}
options.results
This is the actual data to be serialized. In beforeJsonApiSerialize
and
jsonApiSerialize
this will be the raw data as you would ordinarily get it from
loopback. In afterJsonApiSerialize
this will be the serialized data ready for
any final modifications.
options.exclude
This is the exclude settings as defined in the exclude
configuration option
explained earlier. Use this in beforeJsonApiSerialize
to make any model specific
adjustments before serialization.
options.attributes
This is the attributes settings as defined in the attributes
configuration option
explained earlier. Use this in beforeJsonApiSerialize
to make any model specific
adjustments before serialization.
options.data
The raw body data prior to deserialization from creates and updates. This can be
manipulated prior to deserialization using beforeJsonApiDeserialize
options.result
The deserialized raw body data. This is used when saving
models as part of a create or update operation. You can manipulate this prior to
the save occuring in afterJsonApiDeserialize
For occasions when you don't want to fully implement (de)serialization for a model manually but
you need to manipulate the serialization/deserialization process, you can use the
hooks beforeJsonApiSerialize
, afterJsonApiSerialize
, beforeJsonApiDeserialize
and afterJsonApiDeserialize
.
In order to modify the deserialization process on a model by model basis, you can
define a Model.beforeJsonApiDeserialize
function as shown below. The function
will be called with an options object and a callback which must be called with either
an error as the first argument or the modified options object as the second
parameter.
Examples of things you might want to use this feature for
options.data.data.attributes
prior to their being deserialized into model properties that
will be savedoptions.data.data.relationships
prior to their being used to save relationship linkages1module.exports = function (MyModel) { 2 MyModel.beforeJsonApiDeserialize = function (options, callback) { 3 // either return an error 4 var err = new Error('Unwilling to deserialize record'); 5 err.status = 500; 6 callback(err) 7 8 // or return modified data 9 options.data.data.attributes.title = 'modified title'; 10 11 // returned options.data will be deserialized by either the default deserialization process 12 // or by a custom deserialize function if one is present on the model. 13 callback(null, options); 14 } 15}
This function will be called with an options object and a callback which must be called with either an error as the first argument or the modified options object as the second parameter.
Examples of things you might want to use this feature for
options.result
after their having being deserialized from options.data.data.attributes
options.data.data.relationships
prior to their being used to save relationship linkages1module.exports = function (MyModel) { 2 MyModel.afterJsonApiDeserialize = function (options, callback) { 3 // either return an error 4 var err = new Error('something went wrong!'); 5 err.status = 500; 6 callback(err) 7 8 // or return modified data prior to model being saved with options.result 9 options.result.title = 'modified title'; 10 11 callback(null, options); 12 } 13}
options
All config options set for the deserialization process. See the "the options object"
section above for info on what options properties are available for modification.callback
Callback to call with error or options object.In order to modify the serialization process on a model by model basis, you can
define a Model.beforeJsonApiSerialize
function as shown below. The function
will be called with an options object and a callback which must be called with either
an error as the first argument or the modified options object as the second
parameter.
Examples of things you might want to use this feature for
options.results
options.type
options.method
1module.exports = function (MyModel) {
2 MyModel.beforeJsonApiSerialize = function (options, callback) {
3 // either return an error
4 var err = new Error('Unable to serialize record');
5 err.status = 500;
6 callback(err)
7
8 // or return modified records
9 if (Array.isArray(options.results)) {
10 // modify an array of records
11 } else {
12 // modify a single record
13 }
14 // returned options.records will be serialized by either the default serialization process
15 // or by a custom serialize function (described above) if one is present on the model.
16 callback(null, options);
17 }
18}
options
All config options set for the serialization process. See the "function parameters"
section above for info on what options properties are available for modification.callback
Callback to call with error or options object.Because the beforeJsonApiSerialize
method is passed all the options that will
be used during serialization, it is possible to tweak options to affect the
serialization process. One example of this is modifying the type
option to
change the resource type that will be output.
1module.exports = function (MyModel) { 2 MyModel.beforeJsonApiSerialize = function (options, callback) { 3 options.type = 'mycustommodels'; 4 cb(null, options); 5 } 6}
In order to modify the serialized data on a model by model basis, you can
define a Model.afterJsonApiSerialize
function as shown below. The function
will be called with an options object and a callback which must be called with either
an error as the first argument or the modified options object as the second
parameter.
1module.exports = function (MyModel) {
2 MyModel.afterJsonApiSerialize = function (options, callback) {
3 // either return an error
4 var err = new Error('Unable to modify serialized record');
5 err.status = 500;
6 callback(err)
7
8 // or return modified records
9 if (Array.isArray(options.results)) {
10 // modify an array of serialized records
11 } else {
12 // modify a single serialized record
13 }
14 // returned options.records will be output through the api.
15 callback(null, options);
16 }
17}
options
All config options set for the serialization processcallback
Callback to call with modified serialized recordsNo vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
Found 4/13 approved changesets -- score normalized to 3
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2024-12-02
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More