Media types provide the structural representations of API resources. They also
contain a set of views with which you can use to render them. To define a media
type for a resource, create a class that derives from
define its attributes within the
attributes section, and declare one or more
views detailing the subset of the attributes to include. A media type
can also have a human-readable description as well as an associated Internet
media type identifier string.
Here’s an example of a simple media type that describes a few attributes for a
hypothetical Blog API resource. It also defines a couple of views for it, one
default, which displays the common attributes of a Blog, and another
link which only includes the
href attribute, and can be
used when rendering links to Blogs.
Once a media type is defined within your application, you can use it to wrap a
compatible data object holding resource data, and render it using any of the
available views. A compatible object must respond to the method names matching
the media type attribute names, and return sub-objects that are compatible with
the types defined in the media type. Praxis renders media types into
structures to achieve format-independence. These rendered hash structures can
be formatted in your application using the desired wire encoding (JSON, XML, or
any other type you might need).
Here are two examples of how to render a blog_object using the
type: one using its
default view and another using its
In this example, your
blog_object must return:
Praxis provides a lot of help in managing resource objects and linking them to data sources (including databases) by integrating with the Praxis::Mapper gem.
Also, Praxis allows you to generate compatible objects using the
feature of MediaType classes. Using this
.example feature you can create
random instances of compatible objects without any extra effort, which is great
to simulate returning data objects when testing controller responses without
requiring any data source access. There is also some help available for
creating realistic examples for your test cases. See more examples
at the end of this document.
You can specify a description for the media type using the
method. This description string is just for human consumption and is simply inserted directly into to the generated API documentation. However, the documentation browser will appropriately render any markdown or HTML tags that it contains.
The media type identifier method allows you to associate an Internet media type string with the MediaType definition. Internet media types can be very general like ‘application/json’ or specific like ‘application/vnd.acme.blog’:
Identifiers have an optional suffix that indicates the encoding format used to represent the media;
for instance, a blog could be represented as an
application/vnd.acme.blog+json or a
without changing its essential blog-ness. Identifiers can also have semicolon-delimited options
In Praxis, media type identifiers are represented by the
MediaTypeIdentifier class which parses
the identifier’s components and makes them available as instance accessors:
parameters. Identifier objects can be compared, modified, fuzzy-matched against
broader or narrower types, and transformed back into strings.
NOTE: In Praxis 1.0, the
MediaType#identifier DSL method will return a
object and not a String, but it will continue to accept either type of object as a parameter. To
future-proof your Praxis application, simply call
to_s on the return value whenever you call
The attributes section of the media type describes the full structure of the resource representation. It describes the superset of all possible attributes that can appear in any view that can be rendered.
attributes method expects a block of attribute definitions:
Each attribute has a name, type, description and other specific configuration
options: allowed values, format, examples, etc. While some options, such as
values are always available for any attribute, different
attribute types support type-specific options:
max values for Integers,
regexp for Strings, etc.
Similarly to the overall MediaType description, the documentation browser will render attribute
description values as markdown or HTML as appropriate.
To read more about supported types and defining a complex and rich structures, take a look at the Attributor gem and other Praxis media type examples.
A view describes which of the media type attributes should be exposed when that view is rendered.
Each view has a unique name, and there can be as many views in a media type as
you like. Defining a view requires a block that lists the attribute names it
should include. Each included attribute can also take an optional parameter
that defines the view name to use when rendering that specific attribute. A
view option is only available when the type of the attribute is a MediaType
itself. If no view is specified, a view named
default is used. Non-MediaType
attributes don’t support views, so Praxis just renders them by calling
full view above will render all its attributes using their
views, except the
owner attribute, which will be rendered using an
expanded view. In order for this to work, the Person media type needs to have
expanded view defined, detailing what attributes to include.
As a corollary, all media types should include a
default view, as this is the
default view used for rendering.
To include an attribute that has sub-attributes in a view, it is enough to list
its top level name. All of its sub-attributes will follow. For example, the
default view includes the
locale attribute, which will cause its
country sub-attributes to be rendered.
By default, a view will not render attributes whose values are
nil. This can be overrided by passing
include_nil: true when defining the view. For example:
Praxis provides a special helper for crafting media types that refer to other
resources. In particular, it allows the use of the special
links DSL within
the attributes block, where you can list related references. For example:
The main difference between the top-level attributes and the attributes within
links block is that when Praxis renders the views, it will default to
link view. That is, instead of using the
default view when no
view specified, it will render the target attribute using its
link view. For
this to work, the attribute must be a media type itself, and must define a
You can think about the “links” DSL as a way to:
- group links within a ‘links’ substructure
- automatically render their elements using the
Embedding vs. linking
Rendering a Blog media type using the
full view defined above, will result in
embedding two fully rendered media types plus all the other regular attributes
owner attribute embeds a Person media type
rendered using its
default view, and the
blog_entries attribute embeds an
array of BlogEntries, each of them rendered with the
default view. The goal
of providing the
full view is that it directly embeds the whole
representation of related resources. This is the snippet of how the
rendered view will look:
Sometimes it is convenient to provide a lot of embedded information in an API
response as in the example above. This could save the client extra API calls to
retrieve that information later on. But in practice, you don’t want to include
too much unnecessary information in your responses. So it is pretty common to
include only links to embedded information instead. This is exactly what
links in your view will do for you.
This is what the Blog’s
default view renders:
default view for a Blog does not include the
blog_entries as top level attributes. Instead, the
default view includes
them inside the
links attribute. So they will both be rendered using their
link views, which by convention will include a small subset of attributes,
perhaps just the href attribute like the example above. While this works well
for single related members, returning an array of objects with one
is not exactly what you might want for related collections. What you typically
want is to return a single collection href that, if followed, can provide the
contents of the related collection. In the next sections, we’ll cover this in
So far, our examples have only shown that the
link DSL takes a name attribute
which should match an existing and previously-defined attribute. But there are
three different ways to use the
- Provide the name of an existing top-level attribute, like in the examples
above. Using this method, there is no need to specify which associated media
type the link will point to because that can be inferred from the top level
link :ownerwill use the
- Explicitly include both the relationship name as well as
the target MediaType to use. This allows you to link to a related resource,
which might not be listed in the top-level attributes (or one that exists,
but for which you want to use a custom MediaType instead):
link :latest_entry, BlogEntrywhen there is no
- Include a
usingoption to tell Praxis which method name to use to retrieve the data for the link (see the next section for details):
link :super, Person, using: :managerwhich uses the
managermethod rather than the
Defining a link with the
When rendering an attribute, a media type retrieves the value from its
underlying object by calling a method of the same name on that object. For
example, when rendering the
owner attribute, the system will call the
owner method on the underlying object to retrieve the raw data.
There are cases, however, in which the name of the link relationship does not
necessarily correspond to the existing object’s methods. There are other cases
in which you may want to use a relationship name that cannot be easily used as
a Ruby method. In this case, you can use the
using modifier in the
to specify the name of the method to call instead.
Suppose that in the Blogs media type definition example, you want to get the
href for the owner, but you want to call it ‘blogger’ instead of ‘owner’.
You can do this via the
This results in links that look like this:
Note: Technically speaking, the
using modifier refers to the method name of
the object that the MediaType wraps, not a method of the MediaType instance.
In the example above,
using: :owner only works because we know that the
underlying object has that method available. If that method were not available
it would never be able to render its
owner attribute. We could have used
using: :foobar in the example above, as long as the underlying data object
Praxis media types can declare attributes that are ordered collections of other media types or other Attributor types.
Praxis::Collection.of(media_type) defines an inner
Attributor::Collection.of(<media_type>) type inside
Post::Collection) that also includes the
Praxis::MediaTypeCommon module. By default, Praxis will take the identifier of
<media_type> and append a “collection=true” suffix to it. (Note: Previously,
Praxis::Collection.of supported types that did not derive from
MediaType. This usage has been deprecated and will be removed in a future version. Please use
Attributor::Collection.of directly instead.)
For example, your blog media type might have an attribute that is a collection of posts:
When parsing the :posts definition above, Praxis will resolve the type by looking if a
Post::Collection class already exists. If it does, it will be used as the type for the
:posts attribute, otherwise one will be created. One can think of
Praxis::Collection.of(Post) to be equivalent to
Post::Collection, except that it will create one if it does not exist already.
As stated above, the created inner
Collection class will simply be an
Attributor::Collection that wraps members of a given MediaType, and that has an identifier that matches the member’s identifier, with an added “collection=true” suffix. So in the above case, the
Post::Collection will have an identifier of “application/vnd.acme.post;collection=true”.
Collection should be sufficient in most cases. However, you can explicitly define your own and Praxis will use that instead. Here’s an example of how to do that if you wanted
Post::Collection to have an identifier of “application/vnd.acme.posts” instead:
Note: When defined without using the
.of helper, you use the
member_type method to specify what type of media type class this collection is wrapping. If you want a non-anonymous class that is a collection and also has other attributes (such as description), you have two ways to define it:
An Attributor::Collection is a way to embed a collection of another Attributor type. For example, you may want a collection of tag strings to be an attribute of your blog media type:
Praxis expects to find a method named
tags on the underlying object which
must return an array of strings. And when you render this media type, Praxis
will render them as an array of Strings.
Read more about Attributor and Attributor collections in the attributor README file.
Linking to Collections
Because collections are simply arrays of objects, they will not have attributes nor views of their own, which means the normal assumptions for defining and rendering links can not apply. Instead, you should define an inner
Praxis::Blueprint) with the attributes and views you need, and use that for your links.
For example, you might define a
Post::CollectionSummary type for use in links to groupings of
Then you can use that from the
Blog media type in the following way:
Praxis::MediaTypeCollection has been deprecated and will be removed in a future version of Praxis.
The documentation for it has been moved to MediaTypeCollection.
Praxis provides tools to automatically generate example object values that will respond to each and every attribute name of a media type and will return an object that responds to the correct methods of their defined type, including when the attribute type is another media type.
The values of the generated example attributes will also conform to specifications like default values, regexp, etc.