Skip to content

An AdonisJS framework wrapper for https://github.com/danivek/json-api-serializer to have API response formatted according to the JSON API Specification (http://jsonapi.org/)

License

Notifications You must be signed in to change notification settings

dinevillar/adonis-json-api-serializer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Installation

npm i @dinevillar/adonis-json-api-serializer

Setup

Create/edit config/jsonApi.js.

module.exports = {
      "globalOptions": {
          "convertCase": "snake_case",
          "unconvertCase": "camelCase"
      },
      // Register JSON API Types here..
      "registry": {
          "user": {
              "model": 'App/Models/User'
              "structure": {
                  "links": {
                      self: (data) => {
                          return '/users/' + data.id
                      }
                  },
                  "topLevelLinks": {
                      self: '/users'
                  }
              }
          }
      }
  };

Add as provider (start/app.js)

const providers = [
	'@dinevillar/adonis-json-api-serializer/providers/JsonApiProvider'
]

Add to your Models

static get Serializer() {
    return 'JsonApi/Serializer/LucidSerializer'; // Override Lucid/VanillaSerializer
};

Add as named Middleware in start/kernel.js

const namedMiddleware = {
  jsonApi: 'JsonApi/Middleware/Specification'
};

Use in your routes

// All request and response to /user must conform to JSON API v1
Route.resource('user', 'UserController')
    .middleware(['auth', 'jsonApi'])

You can use the "cn" and "ro" schemes of the middleware.

  • Adding "cn" (jsonApi:cn) will allow middleware to check for Content Negotiation
  • Adding "ro" (jsonApi:ro) will allow middleware to check if request body for POST and PATCH conforms with JSON API resource object rules
  • Adding "ro" (jsonApi:ro) also will automatically deserialize resource objects.
  • If none is specified then both will be applied

Usage

model.toJSON():

getUser({request, response}) {
  const user = await User.find(1);
  response.send(user.toJSON());
};

with relations:

config/jsonApi.js

"registry": {
	"company": {
	    "model": 'App/Models/Company',
	    "structure": {
            id: "id",
            links: {
                self: (data) => {
                  return '/companies/' + data.id
                }
            }
		}
	}
	"user": {
	    "model": 'App/Models/User',
	    "structure": {
            "links": {
                self: (data) => {
                    return '/users/' + data.id
                }
            },
            "relationships": {
                company: {
                  type: 'company',
                  links: {
                    self: '/companies'
                  }
                }
            }
            "topLevelLinks": {
                self: '/users'
            }
		}
  	}
 }

App/Models/Company

static get Serializer() {
    return 'JsonApi/Serializer/LucidSerializer';
};

App/Models/User

static get Serializer() {
    return 'JsonApi/Serializer/LucidSerializer';
};

Somewhere:

getUser({request, response}) {
  const user = await User.find(1);
  await user.load('company'); // load relation
  response.send(user.toJSON());
};

Record Browser in your Controllers

const Company = use('App/Models/Company')
const JsonApiRB = use('JsonApiRecordBrowser');
const companies = await JsonApiRB
  .model(Company)
  .request(request.get()) //handle request
  .paginateOrFetch();
response.send(companies.toJSON());

The record browser supports:

Exceptions

You can use JsonApi to handle errors and be able to return valid JSON Api error responses. Create a global ehandler using adonis make:ehandler and use JsonApi in handle() function. See examples/Exception/Handler.js

async handle(error, options) {
    JsonApi.handleError(error, options);
}

Validator

Sample Validator (see examples)

const {formatters} = use('Validator');
const JsonApi = use('JsonApi');
class UserValidator {
    get rules(){
        return {
            'username': 'required|accepted|max:255',
            'contact_number': 'required|accepted|max:50',
            'email': 'required|email|unique:companies,email|max:100'
        }
    }
    
    get formatter() {
        return formatters.JsonApi;
    }
    
    async fails({errors}) {
        for (const error of errors) {
            const jsonError = JsonApi.JSE.UnprocessableResourceObject.invoke();
            jsonError.detail = error.detail;
            jsonError.source = error.source;
            JsonApi.pushError(jsonError);
        }
        return this.ctx.response
            .status(JsonApi.getJsonErrorStatus())
            .send(JsonApi.getJsonError());
    }
}

Serializer Library:

Serializer functions

const {JsonApiSerializer} = use('JsonApi');
const user = await User.find(1);
JsonApiSerializer.serialize("user", user);