JaSerializer.Serializer behaviour
Define a serialization schema.
Provides has_many/2
, has_one/2
, attributes1
and location1
macros
to define how your model (struct or map) will be rendered in the
JSONAPI.org 1.0 format.
Defines format/1
, format/2
and format/3
used to convert models (and
list of models) for encoding in your JSON library of choice.
Example
defmodule PostSerializer do
use JaSerializer
location "/posts/:id"
attributes [:title, :body, :excerpt, :tags]
has_many :comments, link: "/posts/:id/comments"
has_one :author, serializer: PersonSerializer, include: true
def excerpt(post, _conn) do
[first | _ ] = String.split(post.body, ".")
first
end
end
post = %Post{
id: 1,
title: "jsonapi.org + Elixir = Awesome APIs",
body: "so. much. awesome.",
author: %Person{name: "Alan"}
}
post
|> PostSerializer.format
|> Poison.encode!
Summary
attributes(atts) | Defines a list of attributes to be included in the payload |
has_many(name, opts \\ []) | Add a has_many relationship to be serialized |
has_one(name, opts \\ []) | See documentation for has_many/2 |
location(uri) | Defines the canoical path for retrieving this resource |
Types
model :: Map
Macros
Defines a list of attributes to be included in the payload.
An overrideable function for each attribute is generated with the same name as the attribute. The function’s default behavior is to retrieve a field with the same name from the model.
For example, if you have attributes [:body]
a function body/2
is defined
on the serializer with a default behavior of Map.get(model, :body)
.
Add a has_many relationship to be serialized.
Relationships may be included in any of three composeable ways:
- Links
- Resource Identifiers
- Includes
Relationship Source
When you define a relationship, a function is defined of the same name in the
serializer module. This function is overrideable but by default attempts to
access a field of the same name as the relationship on the map/struct passed
in. The field may be changed using the field
option.
For example if you have_many :comments
a function comments2
is defined
which calls Dict.get(model, :comments)
by default.
Link based relationships
Specify a uri which responds with the related resources. See location/1 for defining uris.
The relationship source is disregarded when linking.
defmodule PostSerializer do
use JaSerializer
has_many :comments, link: "/posts/:id/comments"
end
Resource Identifier Relationships
Adds a list of id
and type
pairs to the response with the assumption the
API consumer can use them to retrieve the related resources as needed.
The relationship source should return either a list of ids or maps/structs
that have an id
field.
defmodule PostSerializer do
use JaSerializer
has_many :comments, type: "comments"
def comments(post, _conn) do
post |> PostModel.get_comments |> Enum.map(&(&1.id))
end
end
Included Relationships
Adds a list of id
and type
pairs, just like Resource Indentifier
relationships, but also adds the full serialized resource to the response to
be “sideloaded” as well.
The relationship source should return a list of maps/structs.
defmodule PostSerializer do
use JaSerializer
has_many :comments, serializer: CommentSerializer, include: true
def comments(post, _conn) do
post |> PostModel.get_comments
end
end
defmodule CommentSerializer do
use JaSerializer
has_one :post, field: :post_id, type: "posts"
attributes [:body]
end
See documentation for has_many/2.
API is the exact same.
Defines the canoical path for retrieving this resource.
String Examples
String may be either a full url or a relative path. Path segments begining with a colin are called as functions on the serializer with the model and conn passed in.
defmodule PostSerializer do
use JaSerializer
location "/posts/:id"
end
defmodule CommentSerializer do
use JaSerializer
location "http://api.example.com/posts/:post_id/comments/:id"
def post_id(comment, _conn), do: comment.post_id
end
Atom Example
When an atom is passed in it is assumed it is a function that will return a relative or absolute path.
defmodule PostSerializer do
use JaSerializer
import MyPhoenixApp.Router.Helpers
location :post_url
def post_url(post, conn) do
#TODO: Verify conn can be used here instead of Endpoint
post_path(conn, :show, post.id)
end
end
Callbacks
Specs:
- attributes(model, Plug.Conn.t) :: map
Returns a map of attributes to be mapped.
The default implimentation relies on the attributes/1
macro to define
which fields to be included in the map.
defmodule UserSerializer do
attributes [:email, :name, :is_admin]
end
UserSerializer.attributes(user, conn)
# %{email: "...", name: "...", is_admin: "..."}
You may override this method and use super
to filter attributes:
defmodule UserSerializer do
attributes [:email, :name, :is_admin]
def attributes(model, conn) do
attrs = super(model, conn)
if conn.assigns[:current_user].is_admin do
attrs
else
Map.take(attrs, [:email, :name])
end
end
end
UserSerializer.attributes(user, conn)
# %{email: "...", name: "..."}
You may also skip using the attributes/1
macro altogether in favor of
just defining attributes/2
.
defmodule UserSerializer do
def attributes(model, conn) do
Map.take(model, [:email, :name])
end
end
UserSerializer.attributes(user, conn)
# %{email: "...", name: "..."}
Specs:
The id to be used in the resource object.
http://jsonapi.org/format/#document-resource-objects
Default implimentation attempts to get the :id field from the model.
To override simply define the id function:
def id(model, _conn), do: model.slug
Specs:
- type :: String.t
The type to be used in the resource object.
http://jsonapi.org/format/#document-resource-objects
Default implimentation attempts to infer the type from the serializer module’s name. For example:
MyApp.UserView becomes "user"
MyApp.V1.Serializers.Post becomes "post"
MyApp.V1.CommentsSerializer becomes "comments"
To override simply define the type function:
def type, do: "category"