Using Marshmallow Fields in Open Event API Server
The nextgen Open Event API Server provides API endpoints to fetch the data, and to modify and update it. These endpoints have been written using flask-rest-jsonapi, which is a flask extension to build APIs around the specifications provided by JSONAPI 1.0. This extension helps you, quoting from their website:
flask-rest-jsonAPI’s data abstraction layer lets us expose the resources in a flexible way. This is achieved by using Marshmallow fields by marshmallow-jsonapi. This blog post explains how we use the marshmallow fields for building API endpoints in Open Event API Server.
The marshmallow library is used to serialize, deserialize and validate input data. Marshmallow uses classes to define output schemas. This makes it easier to reuse and configure the and also extend the schemas. When we write the API Schema for any database model from the Open Event models, all the columns have to be added as schema fields in the API class.
This is the API Server’s event schema using marshmallow fields:
These are the Marshmallow Field classes for various types of data. You can pass the following parameters when creating a field object. The ones which are used in the API Server as described below. For the rest, you can read more on marshmallow docs.
Let’s take a look at each of these fields. Each of the following snippets sample writing fields for API Schema.
identifier = fields.Str(dump_only=True)
- This is a field of data-type String.
- dump_only : This field will be skipped during deserialization as it is set to True here. Setting this true essentially means marking `identifier` as read-only( for HTTP API)
name = fields.Str(required=True)
- This again is a field of data-type String.
- This is a required field and a ValidationError is raised if found missing during deserialization. Taking a look at the database backend:
Since this field is set to non-nullable in the database model, it is made required in API Schema.
external_event_url = fields.Url(allow_none=True)
- This is a field of datatype URL.
- Since this is not a required field, NULL values are allowed for this field in the database model. To reflect the same in the API, we have to add allow_none=True. If missing=None is unset, it defaults to false.
ends_at = fields.DateTime(required=True, timezone=True)
- Field of datatype DateTime
- It is a required field for an event and the time used here is timezone aware.
latitude = fields.Float(validate=lambda n: -90 <= n <= 90, allow_none=True)
- Field of datatype Float.
- In marshmallow fields, we can use validator clauses on the input value of a field using the validate: parameter. It returns a boolean, which when false raises a Validation Error. These validators are called during deserialization.
is_map_shown = fields.Bool(default=False)
- Field of datatype boolean.
- Default value for the marshmallow fields can be set by defining using default: Here, is_map_shown attribute is set to false as default for an event.
privacy = fields.Str(default="public")
- privacy is set to default “public”.
When the input value for a field is missing during serialization, the default value will be used. This parameter can either be a value or a callable.
As described in the examples above, you can write the field as field.<data-type>(*parameters to marshmallow.fields.Field constructor*).
The parameters passed to the class constructor must reflect the column definition in the database model, else you might run into unexpected errors. An example to quote from Open Event development would be that null values were not being allowed to be posted even for nullable columns. This behavior was because allow_none defaults to false in schema, and it has to be explicitly set to True in order to receive null values. ( Issue for the same: Make non-required attributes nullable and the Pull Request made for fix.)
Fields represent a database model column and are serialized and deserialized, so that these can be used in any format, like JSON objects which we use in API server. Each field corresponds to an attribute of the object type like location, starts-at, ends-at, event-url for an event. marshmallow allows us to define data-types for the fields, validate input data and reinforce column level constraints from database model.
This list is not exhaustive of all the parameters available for marshmallow fields. To read further about them and marshmallow, check out their documentation.
Additional Resources
- Marshmallow API reference – Marshmallow Docs
- Marshmallow Fields – Marshmallow Docs
- Flask-REST-JSONAPI – Flask-REST-JSONAPI Official Docs
- Marshmallow Schema Basics– Radu Potop
Code involved in API Server:
- Event database model – Open Event Server Developers
- Event API Schema – Open Event Server Developers