Giter Site home page Giter Site logo

jsonapi-server's Introduction

Build Status Coverage Status npm version Dependencies Status

jsonapi-server

Greenkeeper badge

A config driven NodeJS framework implementing json:api and GraphQL. You define the resources, it provides the api.

Motivation / Justification / Rationale

This framework solves the challenges of json:api and GraphQL without coupling us to any one ORM solution. Every other module out there is either tightly coupled to a database implementation, tracking an old version of the json:api spec, or is merely a helper library for a small feature. If you're building an API and your use case only involves reading and writing to a data store... well count yourself lucky. For everyone else, this framework provides the flexibility to provide a complex API without being confined to any one technology.

A config driven approach to building an API enables:

  • Enforced json:api responses
  • Automatic GraphQL schema generation
  • Request validation
  • Payload validation
  • Automatic documentation generation
  • Automatic inclusions
  • Automatic routing
  • Automatic handling of relationships

Ultimately, the only things you as a user of this framework need to care about are:

  • What are my resources called
  • What properties do my resources have
  • For each resource, implement a handler for:
    • createing a resource
    • deleteing a resource
    • searching for many resources
    • finding a specific resource
    • updateing a specific resource

We've created handlers to automatically map our config over to database solutions help people get off the ground:

We've also written a library to ease the consumption of a json:api compliant service, if GraphQL isn't your thing:

Full documentation

The tl;dr

You can have a complete json:api server providing a photos resource with just this:

var jsonApi = require("jsonapi-server");

jsonApi.setConfig({
  port: 16006,
  graphiql: true
});

jsonApi.define({
  resource: "photos",
  handlers: new jsonApi.MemoryHandler(),
  attributes: {
    title: jsonApi.Joi.string(),
    url: jsonApi.Joi.string().uri(),
    height: jsonApi.Joi.number().min(1).max(10000).precision(0),
    width: jsonApi.Joi.number().min(1).max(10000).precision(0)
  }
});

jsonApi.start();

Your new API will be alive at http://localhost:16006/ and your photos resources will be at http://localhost:16006/photos. The GraphiQL interface will be available at http://localhost:16006/.

Show me a full example!

Fire up an example json:api server using the resources mentioned in the official spec via:

$ git clone https://github.com/holidayextras/jsonapi-server.git
$ npm install
$ npm start

then browse to the JSON:API endpoints:

http://localhost:16006/rest/photos

or, for GraphQL:

http://localhost:16006/rest/

the example implementation can be found here

jsonapi-server's People

Contributors

adamlc avatar aledalgrande avatar alunyov avatar brycecicada avatar championswimmer avatar dlras2 avatar duncanfenning avatar greenkeeper[bot] avatar greenkeeperio-bot avatar harryjubb avatar jamesplease avatar jokeyrhyme avatar joseph-norman avatar jpinnix avatar jstafford avatar maxschmeling avatar nihgwu avatar oliversalzburg avatar paparomeo avatar pmcnr-hx avatar richardschneider avatar slickmb avatar st3xupery avatar theninj4 avatar wolfgang42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonapi-server's Issues

New filtering recommendation?

I've just seen this: http://jsonapi.org/recommendations/#filtering which says, as of right now:

The base specification is agnostic about filtering strategies supported by a server. The filter query parameter is reserved to be used as the basis for any filtering strategy.

It's recommended that servers that wish to support filtering of a resource collection based upon associations do so by allowing query parameters that combine filter with the association name.

For example, the following is a request for all comments associated with a particular post:

GET /comments?filter[post]=1 HTTP/1.1
Multiple filter values can be combined in a comma-separated list. For example:

GET /comments?filter[post]=1,2 HTTP/1.1
Furthermore, multiple filters can be applied to a single request:

GET /comments?filter[post]=1,2&filter[author]=12 HTTP/1.1

In master right now, we're using one query parameter to query against a resources attributes and a different one to query against a resources relations, something like this:

GET /comments?relationships[author]=ad3aa89e-9c5b-4ac9-a652-6670f9f27587
GET /comments?filter[timestamp]=2017-01-02

Splitting the two out simplified the initial implementation, but we should move to align with the recommendation. All our relationships links are generated by us for related relationship links on foreign relations.

Lets make supporting of unique fields

Example:

jsonApi.define({
  resource: "example",
  handlers: new jsonApi.MemoryHandler(),
  attributes: {
    title: jsonApi.Joi.string(),
    name: jsonApi.Joi.string().unique(), // When we create or update this field - check for unique and show warnings
  }
});

Question: best way to implement verbose errors on custom authenticate handler

So, I was wondering how I can create errors in the same manner as the built-in way -
for example, when I try to POST to a route and don't pass validation, i get a response like this:

{
  "jsonapi": {
    "version": "1.0"
  },
  "meta": {
    "copyright": "Blah"
  },
  "links": {
    "self": "http://localhost:16006/rest/comments/"
  },
  "errors": [
    {
      "status": "403",
      "code": "EFORBIDDEN",
      "title": "Param validation failed",
      "detail": {
        "name": "ValidationError",
        "details": [
          {
            "message": "\"title\" must be a string",
            "path": "title",
            "type": "string.base",
            "context": {
              "value": 2,
              "key": "title"
            }
          }
        ],
        "_object": {
          "id": "cfdccd45-6395-4b44-b540-41b45b4f3746",
          "type": "comments",
          "title": 2,
          "body": "whatever!!!?"
        }
      }
    }
  ]
}

but when I try to make my custom authenticate route respond like thus:
var currentClientSecret = process.env.CB_CLIENT_SECRET;
jsonApi.authenticate(function(request, callback) {
// console.log(request.headers.authorization);
var authorization = _.get(request, 'headers.authorization', '');
var isBearerToken = authorization.indexOf('Bearer ') > -1;
if(isBearerToken){
var token = authorization.slice(7);
console.log(token);

    try {
      var decoded = jwt.verify(token, new Buffer(currentClientSecret, 'base64'));
      console.log(decoded);
      callback();
    } catch (e) {
      console.log(e);
      callback(e);
    }
  } else {
    var badFormat = new Error('')
    var otherData = {
      status: 401,
      code: 'ENOAUTH',
      title:'Authorization header not in correct format. should be \'Bearer {{token}}\''
    }
    _.assign(badFormat, otherData);
    callback(badFormat);
  }

});

so when I try to test it by submitting a request without the authorization header, I don't get back a verbose response, I get back just a blank 401, no body. is there a way to tap into the nice pattern from above? or should I be implementing authentication differently?

Add Error Handling

Any uncaught exceptions should be handled gracefully.
Developers should be able to implement their own error reporting mechanisms

HTTP 403 returned as default error is not always appropriate

Hi,

I'm using jsonapi-server in my project. However, I wonder if the use of status code HTTP 403 Forbidden is appropriate where it occurs. In particular, my project has no permissions etc, so there is nothing that would forbid a user from working with resources. If it did, I would expect my handlers to manage these permissions... not jsonapi-server itself.

Suggestions:

  1. If I create a handler with no create function, then I have not allowed creation of this resources and POSTing should return HTTP 405 Method Not Allowed. Similarly for update and delete with HTTP PUT and DELETE.
  2. If I POST without any data, then this is a bad request and POSTing should return HTTP 400 Bad Request
  3. If I POST with data that does not match the schema, then this is a bad request and POSTing should return HTTP 400 Bad Request.

Example POST new comment fails with 403 if you specify the article relationship

If I try to POST a new comment with the article relationship specified, it fails with a 403.

I believe the problem is in create.js lines 36-44. Also of note is that in comments.js, article is specified as Joi.belongsToOne. It appears that a POST will fail on any resource if you try to specify the relationship for the Joi.belongsToOne attribute.

curl -X POST --header 'Content-Type: application/vnd.api+json' --header 'Accept: application/vnd.api+json' -d '{
  "data": {
    "attributes": {
      "body": "string"

    },
    "relationships": {
      "article": {
        "data": {
          "type": "articles",
          "id": "de305d54-75b4-431b-adb2-eb6b9e546014",
          "meta": {}
        }
      }
    }
  }
}' 'http://107.170.10.170:16006/rest/comments'

Response body:

{
  "jsonapi": {
    "version": "1.0"
  },
  "meta": {
    "description": "This block shows up in the root node of every payload"
  },
  "links": {
    "self": "http://localhost:16006/rest/comments"
  },
  "errors": [
    {
      "status": "403",
      "code": "EFORBIDDEN",
      "title": "Param validation failed",
      "detail": [
        {
          "message": "\"article\" is not allowed",
          "path": "article",
          "type": "object.allowUnknown",
          "context": {
            "key": "article"
          }
        }
      ]
    }
  ]
}

Spread the Joi

Separate out jsonApi.Joi from jsonApi.js into its own file and move across the core parameter definitions.

Return error details

On a 401, we are not returning any error details passed by the auth hook, just a 401 status.

Example App Startup

Thanks for the great project, it looks nice and I'm looking forward to putting all the work to use. I'm having trouble understanding something in your example though, note - I'm rather new to this environment so it could be simple misunderstanding on my part.

In all of the examples resource and handler files, you require jsonApi as require("../../.") and in server.js there is a server var exported whereas in the readme tldr it does a require("jsonapi-server") and there is no server var defined/exported.

Could you explain the difference, or perhaps point me to a resource that would explain how these 2 work? I've been able to get your example app up and running without issue, but when I try to simply import the package and setup my own project I don't get any of my example data returned and it feels to me that I'm doing this setup potentially incorrectly. Is there a sample showing usage while referenced as package rather than included as project source?

Thanks again, project looks great besides my own troubles getting started!

how to use with mongoose?

I'm trying the lib and want to integrate with my existing mongoose Schema, like below:

var formTypeSchema = mongoose.Schema({

  name:{type:String},
  title:{type:String, default:''},
  mainKey:{type:String, default:'_id'},
  desc:{type:String, default:''},

  createAt:{ type: Date, default: function(){ return new Date() } },
  createBy:{type:_ObjectId , ref:'Person' },

  updateAt:{ type: Date, default: function(){ return new Date() } },
  updateBy:{type:_ObjectId , ref:'Person' },

  template: Mixed

})

with a sample data:

{
  name:"form1",
  title:"test for form1",
  template: { any:"object" }
}

trying but don't know where to start

CORS is enabled, but URLs in links objects are relative.

If a webpage hosted on one server fetches a resource hosted on another (jsonapi-)server, all the links are relative. Plugging these links into ajax requests will at best result in 404 errors, since the webpage will use its own full URL to complete jsonapi-server's relative URL.

For example, a webpage at https://example.com requests resource http://jsonapi.server/api/articles and gets some articles objects. Those articles have a to-one relationship author to people.

The webpage wants to resolve the author of articles/1, so it makes a request to the related link for that relationship, /api/articles/1/author. The request fills in the protocol and host name from it's own location and becomes https://example.com/api/articles/1/author.


I'd suggest making the URLs absolute at request time, rather than during configuration, no need to repeat the DNS; you're basically telling the client, explicitly, that they can make further requests however they made the first one.

Setting relationship while creating resource?

Say I'm creating a blog app and I need to create a new post while setting author to be the current user's object.

Is this possible? It's possible that this issue could actually relate to either the jsonapi-relationaldb-store or the jsonapi-client projects, but in any case, this example from the JSONAPI.org spec:

POST /photos HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "photos",
    "attributes": {
      "title": "Ember Hamster",
      "src": "http://example.com/images/productivity.png"
    },
    "relationships": {
      "photographer": {
        "data": { "type": "people", "id": "9" }
      }
    }
  }
}

seems to yield "photographer" as a null relationship in the result back, and a separate PATCH request must be issued to establish the photographer.

POST a new relationship for a Joi.one("people") in articles fails with 403

If you use your example project, do the following:

  1. POST a new article
  2. POST a new relationship to author of type people to the new article. Here is the curl.
    curl -X POST --header 'Content-Type: application/vnd.api+json' --header 'Accept: application/vnd.api+json' -d '{ "data": { "type": "people", "id": "cc5cca2e-0dd8-4b95-8cfc-a11230e73116", "meta": {} } }' 'http://localhost:16006/rest/articles/41748e1e-ecfe-47a3-b7d8-11a089420cc2/relationships/author'
  3. It will fail with 403. It appears that it does not know the type based on the null in the details messsage.

{
"jsonapi": {
"version": "1.0"
},
"meta": {
"description": "This block shows up in the root node of every payload"
},
"links": {
"self": "http://localhost:16006/rest/articles/41748e1e-ecfe-47a3-b7d8-11a089420cc2/relationships/author"
},
"errors": [
{
"status": "403",
"code": "EFORBIDDEN",
"title": "Param validation failed",
"detail": [
{
"message": ""author" must be one of [null]",
"path": "author",
"type": "any.allowOnly",
"context": {
"valids": [
null
],
"key": "author"
}
},
{
"message": ""author" must be an object",
"path": "author",
"type": "object.base",
"context": {
"key": "author"
}
}
]
}
]
}

Implementing a SQL handler

Hi,

I'm trying to implement an SQL handler.
But I have a problem that I wanted to ask about.

When I'm trying to create a SQL query I want to get a list of all the attributes that I should put in the query. But the attribute list contains things that are not attributes. Such as id, type and meta.
I see in the code that there is some special code that removes the non-attributes from the attribute list when generating the data item.

responseHelper._generateDataItem = function(item, schema) {

  var isSpecialProperty = function(value) {
    if (!(value instanceof Object)) return false;
    if (value._settings) return true;
    return false;
  };
  var linkProperties = Object.keys(schema).filter(function(someProperty) {
    return isSpecialProperty(schema[someProperty]);
  });
  var attributeProperties = Object.keys(schema).filter(function(someProperty) {
    if (someProperty === "id") return false;
    if (someProperty === "type") return false;
    if (someProperty === "meta") return false;
    return !isSpecialProperty(schema[someProperty]);
  });

  var result = {
    type: item.type,
    id: item.id,
    attributes: _.pick(item, attributeProperties),
    links: responseHelper._generateLinks(item, schema, linkProperties),
    relationships: responseHelper._generateRelationships(item, schema, linkProperties),
    meta: item.meta
  };

  return result;
};

It seems strange to me that the attribute list contains the non-attribute entries. And how about if I have an attribute called meta or type? Wouldn't that be a conflict?

The search() function of my handler looks something like this:

        search(request, callback) {
            let attributeKeys = Object.keys(request.resourceConfig.attributes);
            var sql = `
                SELECT ${attributeKeys.join(',')}
                FROM ${request.resourceConfig.resource}
            `;
            db.pool.query(sql, [], (err, res) => {
                let results = !err ? res.rows : [];
                results = _fixResult(request, results);
                return callback(err, results, results.length);
            });
        }

Is there a way to add meta information to a Resource Object or an Identifier?

I need to qualify a relationship with some data, which can be accomplished with the meta object.

However, adding a meta object to an Resource Object Identifier (using the mockHandlers's examples option) results in a ValidationError: "meta" is not allowed, and the server omits the offending Resource Object. The same error is received when attempting to create a Resource Object with meta information.

jsonApi.define({
  resource: "example",
  attributes: {
    qualified_relationship: jsonApi.joi.many("other"),
  },
  examples: [
    {
      type: "example",
      qualified_relationship: [
        {type: "other", id: "1", meta: {quantity: "2"}}
      ],
      meta: {
        description: "It should also be possible to add a `meta` object to a document."
      }
    }
  ]
})

jsonApi.define({
  resource: "other"
  examples: [
    {type: "other", id: "1"}
  ]
});

Joi default() not functioning

using mongodb-store, After set Joi default value as below:

......
attributes: {
    time: api.Joi.date().default(Date.now, 'time of creation'),
    age: api.Joi.number().default(18),
......

if omit the time/age when POST, the database don't have default value specified above.

`self` links within foreign relations don't resolve to the correct resources

In the example folder, start node server.js, with GET below to foreign key parent of tag

A http://localhost:16006/rest/tags/?filter[parent]=6ec62f6d-9f82-40c5-b4f4-279ed1765492

......
"data": {
        "id": "7541a4de-4986-4597-81b9-cf31b6762486",
        "type": "tags"  //this res.name == 'live', which is not direct children of 279ed1765492
    },
......

B http://localhost:16006/rest/tags/relationships/?parent=6ec62f6d-9f82-40c5-b4f4-279ed1765492

......
 "data": [{
            "type": "tags",
            "id": "2a3bdea4-a889-480d-b886-104498c86f69",
            "attributes": {
                "name": "staging"
            }
......

should A && B return same version of data? now the returned data of A seems not correctly.

Rename `CHANGELOG.md` or make it Markdown

Either rename CHANGELOG.md to CHANGELOG.txt or make it proper Markdown, otherwise GitHub renders it nastily. While we're at it, it'll probably make sense to reorder in chronologically inverse order with the most recent entries first.

How should we deal with pagination?

  • How should a data handler communicate to jsonapi-server "Here's the first 50 of 100 records"?
  • How should we deal with pagination of included resources?
  • Should we allow the first XX included resources and drop the rest?
  • Should we error on the request if we can't fulfil every requested include?

Access to inner server

There seems to be no way of accessing the inner server of this module. Even to use an existing express instance needs to mess with the require cache (as per the example in the docs).

I particularly want to run a Websockets server in the same host and port, which would be possible if either I had access to the inner server or better yet: if I could specify a server in setConfig().

Return all validation errors instead of only the first failure

It would be useful if all validation errors were returned instead of only the first failure. Looking through the code it looks like this would be pretty straightforward by passing the {abortEarly: false} option to Joi.validate() and then iterating through each of the errors to create each error object.

Included relationships fail when host and protocol are unspecified.

To reproduce:

  1. Run the example server
  2. Query http://localhost:16006/articles?include=author
  3. Observe the included people are included
  4. Remove protocol and hostname from setConfig in index.js
  5. Rerun the above query
  6. Observe the included people are now missing

While including the protocol and hostname fixes the inclusions, it also prepends it to all the links. This is fine as long as the outgoing links match the consuming service, but doing any sort of port mapping, load balancing, hostname redirects, etc, means that the exposed links do not include the expected hostnames. Leaving the links host-less allows this, and is allowed by the spec (e.g.)

Improve the efficiency of complex inclusions

Right now, an inclusion such as:

http://localhost:16006/rest/articles?include=author.photos,photos

Can be surprisingly expensive due to the naive implementation of inclusions which simply follows (many) related links. We can group a lot of these requests together and drastically reduce the complexity.

$ DEBUG=jsonApi:* ./node_modules/.bin/mocha ./test/get-\:resource.js 
...
  jsonApi:validation:input {"type":"articles","include":"author.photos,photos"} +21ms
  jsonApi:handler:search {"type":"articles","include":"author.photos,photos"} +1ms [null,[{"id":"de305d54-75b4-431b-adb2-eb6b9e546014","type":"articles","title":"NodeJS Best Practices","content":"na","author":{"type":"people","id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","meta":{"updated":"2010-11-06"}},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[],"comments":[{"type":"comments","id":"3f1a89c2-eb85-4799-a048-6735db24b7eb"}],"meta":{"updated":"2011-05-10"}},{"id":"1be0913c-3c25-4261-98f1-e41174025ed5","type":"articles","title":"Linux Rocks","content":"na","author":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"},"tags":[{"type":"tags","id":"2a3bdea4-a889-480d-b886-104498c86f69"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"},{"type":"photos","id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8"}],"comments":[]},{"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"articles","title":"How to AWS","content":"na","author":{"type":"people","id":"32fb0105-acaa-4adb-9ec4-8b49633695e1"},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"}],"comments":[]},{"id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","type":"articles","title":"Tea for Beginners","content":"na","author":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"},"tags":[{"type":"tags","id":"6ec62f6d-9f82-40c5-b4f4-279ed1765492"},{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0"}],"comments":[{"type":"comments","id":"6b017640-827c-4d50-8dcc-79d766abb408"}]}]]
  jsonApi:validation:output {"id":"de305d54-75b4-431b-adb2-eb6b9e546014","type":"articles","title":"NodeJS Best Practices","content":"na","author":{"type":"people","id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","meta":{"updated":"2010-11-06"}},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[],"comments":[{"type":"comments","id":"3f1a89c2-eb85-4799-a048-6735db24b7eb"}],"meta":{"updated":"2011-05-10"}} +2ms
  jsonApi:validation:output {"id":"1be0913c-3c25-4261-98f1-e41174025ed5","type":"articles","title":"Linux Rocks","content":"na","author":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"},"tags":[{"type":"tags","id":"2a3bdea4-a889-480d-b886-104498c86f69"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"},{"type":"photos","id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8"}],"comments":[]} +0ms
  jsonApi:validation:output {"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"articles","title":"How to AWS","content":"na","author":{"type":"people","id":"32fb0105-acaa-4adb-9ec4-8b49633695e1"},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"}],"comments":[]} +1ms
  jsonApi:validation:output {"id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","type":"articles","title":"Tea for Beginners","content":"na","author":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"},"tags":[{"type":"tags","id":"6ec62f6d-9f82-40c5-b4f4-279ed1765492"},{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0"}],"comments":[{"type":"comments","id":"6b017640-827c-4d50-8dcc-79d766abb408"}]} +0ms
  jsonApi:include http://localhost:16006/rest/articles/de305d54-75b4-431b-adb2-eb6b9e546014/author?& +1ms
  jsonApi:include http://localhost:16006/rest/articles/de305d54-75b4-431b-adb2-eb6b9e546014/photos?& +0ms
  jsonApi:include http://localhost:16006/rest/articles/1be0913c-3c25-4261-98f1-e41174025ed5/author?& +1ms
  jsonApi:include http://localhost:16006/rest/articles/1be0913c-3c25-4261-98f1-e41174025ed5/photos?& +0ms
  jsonApi:include http://localhost:16006/rest/articles/d850ea75-4427-4f81-8595-039990aeede5/author?& +0ms
  jsonApi:include http://localhost:16006/rest/articles/d850ea75-4427-4f81-8595-039990aeede5/photos?& +0ms
  jsonApi:include http://localhost:16006/rest/articles/fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5/author?& +1ms
  jsonApi:include http://localhost:16006/rest/articles/fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5/photos?& +0ms
  jsonApi:handler:find {"type":"articles","id":"de305d54-75b4-431b-adb2-eb6b9e546014","relation":"author"} +6ms [null,{"id":"de305d54-75b4-431b-adb2-eb6b9e546014","type":"articles","title":"NodeJS Best Practices","content":"na","author":{"type":"people","id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","meta":{"updated":"2010-11-06"}},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[],"comments":[{"type":"comments","id":"3f1a89c2-eb85-4799-a048-6735db24b7eb"}],"meta":{"updated":"2011-05-10"}}]
  jsonApi:handler:find {"type":"articles","id":"de305d54-75b4-431b-adb2-eb6b9e546014","relation":"photos"} +0ms [null,{"id":"de305d54-75b4-431b-adb2-eb6b9e546014","type":"articles","title":"NodeJS Best Practices","content":"na","author":{"type":"people","id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","meta":{"updated":"2010-11-06"}},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[],"comments":[{"type":"comments","id":"3f1a89c2-eb85-4799-a048-6735db24b7eb"}],"meta":{"updated":"2011-05-10"}}]
  jsonApi:handler:find {"type":"articles","id":"1be0913c-3c25-4261-98f1-e41174025ed5","relation":"author"} +0ms [null,{"id":"1be0913c-3c25-4261-98f1-e41174025ed5","type":"articles","title":"Linux Rocks","content":"na","author":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"},"tags":[{"type":"tags","id":"2a3bdea4-a889-480d-b886-104498c86f69"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"},{"type":"photos","id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8"}],"comments":[]}]
  jsonApi:handler:find {"type":"articles","id":"1be0913c-3c25-4261-98f1-e41174025ed5","relation":"photos"} +0ms [null,{"id":"1be0913c-3c25-4261-98f1-e41174025ed5","type":"articles","title":"Linux Rocks","content":"na","author":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"},"tags":[{"type":"tags","id":"2a3bdea4-a889-480d-b886-104498c86f69"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"},{"type":"photos","id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8"}],"comments":[]}]
  jsonApi:handler:find {"type":"articles","id":"d850ea75-4427-4f81-8595-039990aeede5","relation":"author"} +0ms [null,{"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"articles","title":"How to AWS","content":"na","author":{"type":"people","id":"32fb0105-acaa-4adb-9ec4-8b49633695e1"},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"}],"comments":[]}]
  jsonApi:handler:find {"type":"articles","id":"d850ea75-4427-4f81-8595-039990aeede5","relation":"photos"} +0ms [null,{"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"articles","title":"How to AWS","content":"na","author":{"type":"people","id":"32fb0105-acaa-4adb-9ec4-8b49633695e1"},"tags":[{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"}],"comments":[]}]
  jsonApi:handler:find {"type":"articles","id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","relation":"author"} +0ms [null,{"id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","type":"articles","title":"Tea for Beginners","content":"na","author":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"},"tags":[{"type":"tags","id":"6ec62f6d-9f82-40c5-b4f4-279ed1765492"},{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0"}],"comments":[{"type":"comments","id":"6b017640-827c-4d50-8dcc-79d766abb408"}]}]
  jsonApi:handler:find {"type":"articles","id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","relation":"photos"} +0ms [null,{"id":"fa2a073f-8c64-4cbb-9158-b8f67a4ab9f5","type":"articles","title":"Tea for Beginners","content":"na","author":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"},"tags":[{"type":"tags","id":"6ec62f6d-9f82-40c5-b4f4-279ed1765492"},{"type":"tags","id":"7541a4de-4986-4597-81b9-cf31b6762486"}],"photos":[{"type":"photos","id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0"}],"comments":[{"type":"comments","id":"6b017640-827c-4d50-8dcc-79d766abb408"}]}]
  jsonApi:handler:find {"type":"people","id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116"} +7ms [null,{"id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","type":"people","firstname":"Oli","lastname":"Rumbelow","email":"[email protected]"}]
  jsonApi:handler:find {"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"} +1ms [null,{"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"people","firstname":"Mark","lastname":"Fermor","email":"[email protected]"}]
  jsonApi:handler:find {"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"} +0ms [null,{"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}}]
  jsonApi:handler:find {"type":"photos","id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8"} +0ms [null,{"id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8","type":"photos","title":"Penguins","url":"http://www.example.com/penguins","height":220,"width":60,"photographer":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"}}]
  jsonApi:handler:find {"type":"people","id":"32fb0105-acaa-4adb-9ec4-8b49633695e1"} +0ms [null,{"id":"32fb0105-acaa-4adb-9ec4-8b49633695e1","type":"people","firstname":"Pedro","lastname":"Romano","email":"[email protected]"}]
  jsonApi:handler:find {"type":"photos","id":"aab14844-97e7-401c-98c8-0bd5ec922d93"} +0ms [null,{"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}}]
  jsonApi:handler:find {"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"} +0ms [null,{"id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587","type":"people","firstname":"Rahul","lastname":"Patel","email":"[email protected]"}]
  jsonApi:handler:find {"type":"photos","id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0"} +0ms [null,{"id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0","type":"photos","title":"Cup of Tea","url":"http://www.example.com/treat","height":350,"width":350,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}}]
  jsonApi:validation:output {"id":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116","type":"people","firstname":"Oli","lastname":"Rumbelow","email":"[email protected]"} +8ms
  jsonApi:validation:output {"id":"d850ea75-4427-4f81-8595-039990aeede5","type":"people","firstname":"Mark","lastname":"Fermor","email":"[email protected]"} +1ms
  jsonApi:validation:output {"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +1ms
  jsonApi:validation:output {"id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8","type":"photos","title":"Penguins","url":"http://www.example.com/penguins","height":220,"width":60,"photographer":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"}} +1ms
  jsonApi:validation:output {"id":"32fb0105-acaa-4adb-9ec4-8b49633695e1","type":"people","firstname":"Pedro","lastname":"Romano","email":"[email protected]"} +1ms
  jsonApi:validation:output {"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +0ms
  jsonApi:validation:output {"id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587","type":"people","firstname":"Rahul","lastname":"Patel","email":"[email protected]"} +1ms
  jsonApi:validation:output {"id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0","type":"photos","title":"Cup of Tea","url":"http://www.example.com/treat","height":350,"width":350,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +0ms
  jsonApi:include http://localhost:16006/rest/photos/?relationships[photographer]=cc5cca2e-0dd8-4b95-8cfc-a11230e73116& +13ms
  jsonApi:include http://localhost:16006/rest/photos/?relationships[photographer]=d850ea75-4427-4f81-8595-039990aeede5& +0ms
  jsonApi:include http://localhost:16006/rest/photos/?relationships[photographer]=32fb0105-acaa-4adb-9ec4-8b49633695e1& +1ms
  jsonApi:include http://localhost:16006/rest/photos/?relationships[photographer]=ad3aa89e-9c5b-4ac9-a652-6670f9f27587& +0ms
  jsonApi:validation:input {"type":"photos","relationships":{"photographer":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116"}} +3ms
  jsonApi:validation:input {"type":"photos","relationships":{"photographer":"d850ea75-4427-4f81-8595-039990aeede5"}} +0ms
  jsonApi:validation:input {"type":"photos","relationships":{"photographer":"32fb0105-acaa-4adb-9ec4-8b49633695e1"}} +1ms
  jsonApi:validation:input {"type":"photos","relationships":{"photographer":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +0ms
  jsonApi:handler:search {"type":"photos","relationships":{"photographer":"cc5cca2e-0dd8-4b95-8cfc-a11230e73116"}} +0ms [null,[]]
  jsonApi:handler:search {"type":"photos","relationships":{"photographer":"d850ea75-4427-4f81-8595-039990aeede5"}} +0ms [null,[{"id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8","type":"photos","title":"Penguins","url":"http://www.example.com/penguins","height":220,"width":60,"photographer":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"}}]]
  jsonApi:handler:search {"type":"photos","relationships":{"photographer":"32fb0105-acaa-4adb-9ec4-8b49633695e1"}} +1ms [null,[]]
  jsonApi:handler:search {"type":"photos","relationships":{"photographer":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +0ms [null,[{"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}},{"id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0","type":"photos","title":"Cup of Tea","url":"http://www.example.com/treat","height":350,"width":350,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}}]]
  jsonApi:validation:output {"id":"72695cbd-e9ef-44f6-85e0-0dbc06a269e8","type":"photos","title":"Penguins","url":"http://www.example.com/penguins","height":220,"width":60,"photographer":{"type":"people","id":"d850ea75-4427-4f81-8595-039990aeede5"}} +4ms
  jsonApi:validation:output {"id":"aab14844-97e7-401c-98c8-0bd5ec922d93","type":"photos","title":"Matrix Code","url":"http://www.example.com/foobar","height":1080,"width":1920,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +1ms
  jsonApi:validation:output {"id":"4a8acd65-78bb-4020-b9eb-2d058a86a2a0","type":"photos","title":"Cup of Tea","url":"http://www.example.com/treat","height":350,"width":350,"photographer":{"type":"people","id":"ad3aa89e-9c5b-4ac9-a652-6670f9f27587"}} +0ms
        โœ“ include author.photos and photos (75ms)

urlencoded query parameters broken

As reported by @jbockerstette here, urlencoded query parameters (as those generated by swagger.io) are not being handled correctly:

Pedro,

Here is the real problem.

If you paste this into the browser it works as expected.
http://107.170.10.170:16006/1/articles?fields[articles]=title

However, if you use swagger.io UI, swagger turns the above statement to
this:
http://107.170.10.170:16006/1/articles?fields=%5Barticles%5D%3Dtitle

swagger encodes the brackets and equals into %5B, %5D and %3D. It appears
that this encoding is breaking it. Maybe all you need to do is decode it
before processing.

I apologize for poor problem definition previously.

Jim

Enable Authentication

Allow developers to authenticate every request and either allow it to proceed or block it with a 401 Unauthorized.

Unable to patch relationships on resources with required attributes

JSON:API spec for patching relationships is described at http://jsonapi.org/format/#crud-updating-relationships.

Updating To-Many Relationships
And the following request clears every tag for an article:

PATCH /articles/1/relationships/tags HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json           
{
  "data": []
}

However, attempting this on resources with required attributes results in HTTP 403 Forbidden with detail "Param validation failed".

This can be reproduced on the example project with:

PATCH http://localhost:16006/rest/articles/de305d54-75b4-431b-adb2-eb6b9e546014/relationships/tags
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json           
{
  "data": []
}

which results in:

Status Code: 403 Forbidden
{
  "jsonapi": {
    "version": "1.0"
  },
  "meta": {
    "description": "This block shows up in the root node of every payload"
  },
  "links": {
    "self": "http://localhost:16006/rest/articles/de305d54-75b4-431b-adb2-eb6b9e546014/relationships/tags"
  },
  "errors": [
    {
      "status": "403",
      "code": "EFORBIDDEN",
      "title": "Param validation failed",
      "detail": [
        {
          "message": "\"title\" is required",
          "path": "title",
          "type": "any.required",
          "context": {
            "key": "title"
          }
        },
        {
          "message": "\"content\" is required",
          "path": "content",
          "type": "any.required",
          "context": {
            "key": "content"
          }
        }
      ]
    }
  ]

POST and PATCH don't support the required content-type header

The json:api specifications state that

Clients MUST send all JSON API data in request documents with the header Content-Type: application/vnd.api+json without any media type parameters.

But the jsonapi-server doesn't work if application/vnd.api+json is used. It works is application/json is used however.

POST http://localhost:16006/rest/photos HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "photos",
    "attributes": {
        "title": "dsole",
        "url": "http://www.example.com/dsole",
        "height": 1024,
        "width": 768
    }
  }
}

return status 403 Forbidden

HTTP/1.1 403 Forbidden
Content-Type: application/vnd.api+json; charset=utf-8
Location: https://localhost:16006/rest/photos
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Content-Length: 213
Connection: keep-alive

{
   "meta":{
      "copyright":"Blah"
   },
   "links":{
      "self":"/rest/photos"
   },
   "errors":[
      {
         "status":"403",
         "code":"EFORBIDDEN",
         "title":"Request validation failed",
         "detail":"Missing \"data\" - have you sent the right http headers?"
      }
   ]
}

With the Content-Type header set to application/json in the request, the request returns the expected 201 Created.
This undesirable behaviour is being verified by the tests. For example:

    describe("creates a resource", function() {
      var id;

      it("works", function(done) {
        var data = {
          method: "post",
          url: "http://localhost:16006/rest/photos",
          headers: {
            "Content-Type": "application/json"
          },

swagger.json generated by the example has errors

Swagger generation doesn't seem to work correctly. The generated swagger.json contains may errors from the example project.

In order to debug this problem, I eliminated all the resources except for the people.js resource. In addition, I got rid of references to the other resources in the people.js file.

I then used the swagger editor to fix the problems found in the resulting swagger.json

I am providing the links to my people.js file : https://drive.google.com/file/d/0BzXg1kzQ457OeHpUMVhtVDk2MEk/view?usp=drivesdk

Here is the swagger.json file that was generated: https://drive.google.com/file/d/0BzXg1kzQ457ONDlVZUZZOElZdUE/view?usp=drivesdk

Here is the swagger.json file that I fixed using the Swagger editor: https://drive.google.com/file/d/0BzXg1kzQ457ORVdpdGlQRU90bVk/view?usp=drivesdk

Related data not being included

I have the following server:

var jsonApi = require("jsonapi-server");
var MongoStore = require("jsonapi-store-mongodb");


jsonApi.setConfig({
  protocol: "http",
  hostname: "localhost",
  port: 3000,
  base: "api",
});

jsonApi.define({
  resource: "authors",
  handlers: new MongoStore({
    url: "mongodb://localhost:27017/book-collection-example",
  }),
  attributes: {
    name: jsonApi.Joi.string(),
    books: jsonApi.Joi.belongsToMany({
      resource: "books",
    }),
  }
});

jsonApi.define({
  resource: "books",
  handlers: new MongoStore({
    url: "mongodb://localhost:27017/book-collection-example",
  }),
  attributes: {
    title: jsonApi.Joi.string(),
    author: jsonApi.Joi.one("authors"),
  }
});


jsonApi.start();

If I add the following author and book:

curl -v -H "Content-Type: application/vnd.api+json" 0.0.0.0:3000/api/authors -d '{"data": { "attributes": { "name": "Ernest Cline" } } }'

curl -v -H "Content-Type: application/vnd.api+json" 0.0.0.0:3000/api/books -d '{"data": { "attributes": { "title": "Ready Player One" }, "relationships": { "author": { "data": { "type": "author", "id": "<insert-id-here>" } } } } }'

They are successfully added to the database.

The problem is when I retrieve all authors, the related data isn't included:

curl -v -H "Content-Type: application/vnd.api+json" 0.0.0.0:3000/api/authors

{"jsonapi":{"version":"1.0"},"meta":{"page":{"offset":0,"limit":50,"total":1}},"links":{"self":"http://localhost:3000/api/authors"},"data":[{"type":"authors","id":"af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f","attributes":{"name":"Ernest Cline"},"links":{"self":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f"},"relationships":{"books":{"meta":{"relation":"primary","readOnly":false},"links":{"self":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f/relationships/books","related":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f/books"},"data":[]}}}],"included":[]}

If I try to specify that I want the books from a given author included, I get the same reponse:

curl -v -H "Content-Type: application/vnd.api+json" 0.0.0.0:3000/api/authors?include=books

{"jsonapi":{"version":"1.0"},"meta":{"page":{"offset":0,"limit":50,"total":1}},"links":{"self":"http://localhost:3000/api/authors?include=books"},"data":[{"type":"authors","id":"af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f","attributes":{"name":"Ernest Cline"},"links":{"self":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f"},"relationships":{"books":{"meta":{"relation":"primary","readOnly":false},"links":{"self":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f/relationships/books","related":"http://localhost:3000/api/authors/af5e3405-1d9c-4996-8e8d-8c8f0e2dac1f/books"},"data":[]}}}],"included":[]}

I'm expecting the books relationship data field to contain the books that are related to the given author.

Allow resource namespaces in URL

HI,

Suppose my resources are logically grouped in some way. Say, I have a groups of resources relating to travel and some other group of resources relating to finance. Conceptually, this is like having different namespaces for resources. Is it possible in jsonapi-server to have this logical structure of resources represented in the url? eg

I can only see how to configure a base url http://www.myserver.com/resources/ and then all resources are presumed to hang directly from that, like http://www.myserver.com/resources/schedules etc, rather than in some logical grouping of sub-urls including travel and finance

Right now, the only way I can think of achieving this is to put a reverse proxy in front of (perhaps multiple) jsonapi-server instances. The reverse proxy can then route the urls. Seems a bit overkill. Is there a better way?

Is it worth me investigating such a change to jsonapi-service and send a PR?

Separate out postProcessing

Split the 4x main features of postProcessing.js (sort, filter, include, fields) out into separate files to improve readability.

Swagger UI is generating curls that are not understood by jsonapi-server

I have reported this bug to both projects (jsonapi-server) and swagger-ui project. Each project has responded that it is not a bug in their software. I am not an expert in this subject. All I know is that swagger does not work correctly with jsonapi-server when trying to pass params from the swagger ui to jsonapi-server. Please take a look at this issue and let me know what I should do. Thanks.
swagger-api/swagger-ui#2159

Null pointer

/root/jsonapi-server/lib/routes/relationships.js:47
sanitisedData = sanitisedData.relationships[request.params.relation].data;
^

TypeError: Cannot read property 'relationships' of null
at /root/jsonapi-server/lib/routes/relationships.js:47:38
at fn (/root/jsonapi-server/node_modules/async/lib/async.js:746:34)
at /root/jsonapi-server/node_modules/async/lib/async.js:1213:16
at /root/jsonapi-server/node_modules/async/lib/async.js:166:37
at /root/jsonapi-server/node_modules/async/lib/async.js:706:43
at /root/jsonapi-server/node_modules/async/lib/async.js:167:37
at Immediate._onImmediate (/root/jsonapi-server/node_modules/async/lib/async.js: 1206:34)
at tryOnImmediate (timers.js:534:15)
at processImmediate as _immediateCallback

Enhancement Request: Allow logging via bunyan

I don't know the best way to even ask this, but how would I create an attach a bunyan logger? one of the features I really liked about restify was that it created a child logger on each request - Restify API and scroll down to request logger

allowing for very simple commands, like at the beginning of a handler, writing
req.log.info({req:req}, 'attemping to do action X');

and at the end, writing a success or failure depending on your outcome -

I'd love to help out by offering a PR, but being a bit green I don't even know where I'd start.

Thanks in advance,
Paul

Expected behaviour of combining include + filter

Suppose there are 3x resources, a, b and c:

  • a has many b
  • b has many c

We could query across these resources with a request similar to this:
https://api.example.com/a?include=b.c

Suppose we then add a filter to only include resources of b which have property foo=bar:
https://api.example.com/a?include=b.c&filter[b][foo]=bar

Should the response contain either:

  • Every a resource

OR

  • Only the a resources who reference b resources with the property foo=bar

Null relationships have a self link that results in an uncaught exception if retrieved

Using the example app as the starting point, I modeled a folder heirarchy using a "folders" resource that refers to another "folders" resource through a parent attribute. There is a case where a folders resource doesn't have a parent as it will be the root folder. The folders resource can be retrieved and shows the following relationship attribute for parent:

parent": {
    "meta": 
   {
      "relation": "primary",
      "readOnly": false
   },
   "links": 
    {
        "self": "http://localhost:8080/rest/folders/6b017640-827c-4d50-8dcc-79d766abb408/relationships/parent",
        "related": "http://localhost:8080/rest/folders/6b017640-827c-4d50-8dcc-79d766abb408/parent"
    },
    "data": null
}

The problem is if you follow the "related" link it will crash the server with the following exception...

/jsonapi-test/node_modules/jsonapi-server/lib/postProcess.js:41
    map[dataItem.type] = map[dataItem.type] || [ ];
                ^

TypeError: Cannot read property 'type' of null
    at /jsonapi-test/node_modules/jsonapi-server/lib/postProcess.js:41:17
    at Array.reduce (native)
    at Object.postProcess._fetchRelatedResources (/jsonapi-test/node_modules/jsonapi-server/lib/postProcess.js:40:36)
    at async.waterfall.relatedResources (/jsonapi-test/node_modules/jsonapi-server/lib/routes/related.js:51:21)
    at fn (/jsonapi-test/node_modules/async/lib/async.js:746:34)
    at /jsonapi-test/node_modules/async/lib/async.js:1213:16
    at /jsonapi-test/node_modules/async/lib/async.js:166:37
    at /jsonapi-test/node_modules/async/lib/async.js:706:43
    at /jsonapi-test/node_modules/async/lib/async.js:167:37
    at Immediate._onImmediate (/jsonapi-test/node_modules/async/lib/async.js:1206:34)

It seems to me a valid use case but in this case the links attribute would need to be removed but the issue would still remain that if somebody manually creates their own link it will bring down the server.

I've put up a test repository showing my set up at https://github.com/dalane/jsonapi-test

Unique validator?

Hi, just getting started with this incredible lib, Is there an unique validator?

server responds to OPTIONS method with 404

The browser is sending a preflight OPTIONS request that returns 404. This seems to be a CORS. I could very well be doing something wrong but cannot find any docs on how to implement CORS on the server.

providing links on the root endpoint

I was reviewing the example and the code and I was expecting the different endpoints to be available on the api root as links. When using hypermedia apis, I expose all available endpoints in the links collection of the root endpoint. That way clients only know one url to access the api, all others are provided.

If I wanted to add a resource that would be exposed on the api root, how would I do that? And how would I set what links or embed resources on the root?

Thanks

Question: best way to handle authorization - i.e. who can edit a file/post for routes

I was also wondering if you guys have any plans / ideas for how to handle authorization? I'm curious if there's any best practices you guys operate with.

I was wondering if I should just implement jsonapi-server and write a seperate front-end utilizing jsonapi-client? Am I asking for too much customization out of an already very solid code-base?

Thanks,
Paul

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.