Serializers

Serializers provides clean abstractions towards structuring the database results. AdonisJs by default ships with a Vanilla serializer, but you are free to create/use other serializers too.

For example: You can create a serializer to format data as per jsonapi specs.

Introduction

Every time you make a database query using Lucid models, the return value is always a serializer instance and calling toJSON to it returns a formatted output.

const User = use('App/Models/User')

const users = await User.all()

// users -> Vanilla Serializer instance

The users object is an instance of Vanilla serializer and in order to get a plain array/object from it, you need to call toJSON() method.

const json = users.toJSON()

Why use serializers?

When writing an API server, the data returned from models is not something that you want to return back and instead follow some conventions to structure the data.

The serializer solves that problem by giving the freedom to format the data as you want. For example

const User = use('App/Models/User')

const users = await User
  .query()
  .with('posts')
  .fetch()

At this point Lucid loads all the users and their posts, but doesn’t care on how to present them in JSON format, but instead pass all the data to the serializer, which can be accessed from users constant.

Now when you call toJSON on users. The vanilla serializer formats the data as follows.

users.toJSON()

// output
[
  {
    id: 1,
    username: 'virk',
    posts: [
      {
        id: 1,
        user_id: 1,
        title: 'Adonis 101'
      }
    ]
  }
]

Also, it executes all the getters, setters and computed properties for you.

Using Serializer

The serializers can be defined on each model by overriding the Serializer getter.

class User extends Model {
  static get Serializer () {
    return // your own implementation
  }
}

Vanilla Serializer

Here is the list of operations vanilla serializer performs for you.

  1. Attach all relations next to the row as a property.

  2. Attach all sideloaded data to the __meta__ key. For example posts counts for a given user are represented as

    {
      id: 1,
      username: 'virk',
      __meta__: {
        posts_count: 2
      }
    }
  3. Format pagination results as.

    {
      total: 10,
      perPage: 20,
      lastPage: 1,
      currentPage: 1,
      data: []
    }

Creating Serializer

You can create your serializer to get data back in your desired format. Intentionally the API of serializers is pretty small, to make it easier to add and use new serializers.

Serializers should not be created for small amendments to the output, use getters and computed properties for that.

API overview

Below is the list of methods, which are required on a serializer.

class CustomSerializer {
  constructor (rows, pages = null, isOne = false) {
    this.rows = rows
    this.pages = pages
    this.isOne = isOne
  }

  first () {
    return this.rows[0]
  }

  last () {
    return this.rows[this.rows.length - 1]
  }

  size () {
    return this.isOne ? 1 : this.rows.length
  }

  toJSON () {
    // return formatted data
  }
}

module.exports = CustomSerializer

Once done, you can import this serializer manually using require statement or bind it to the IoC container.

start/hooks.js
const { ioc } = require('@adonisjs/fold')

ioc.bind('MyApp/CustomSerializer', () => {
  require('./CustomSerializer')
})

Use it as

class User extends Model {
  static get Serializer () {
    return 'MyApp/CustomSerializer'
  }
}