Before you read this section, you should take a look at the APIMAS specification and predicates.
Suppose that you want to build a simple e-shop application which provide a REST API to its clients. A typical e-shop application has the following entities which are described by some fields:
users
: Users of the e-shop application which hold carts,
make orders and buy products.products
: A list of products described with a product key,
price, a name, a description and a quantity.carts
: Carts which contain a list of products.orders
: Orders made by users to purchase a specific cart.Given the above, representing your application’s REST API is an easy task. First, you need to identify the collections of your application.
In this context of e-shop, consider the following collections of resource corresponding to every entity, and the following actions performed on them.
users
:
POST /api/users/
GET /api/users/<pk>/
PUT /api/users/<pk>/
PATCH /api/users/<pk>/
products
:
GET /api/products/
GET /api/products/<pk>/
carts
:
POST /api/carts/
GET /api/carts/
GET /api/carts/<pk>/
PUT /api/carts/<pk>/
PATCH /api/carts/<pk>/
DELETE /api/carts/<pk>/
orders
:
POST /api/orders/
GET /api/orders/
GET /api/orders/<pk>/
The above REST representation of your application is described by the following specification document.
API_SPEC = {
'api': {
'.endpoint': {},
'users': {
'.collection': {},
'.actions': {
'.retrieve': {},
'.create': {},
'.update': {},
}
},
'products': {
'.collection': {},
'.actions': {
'.list': {},
'.retrieve': {},
}
},
'carts': {
'.collection': {},
'.actions': {
'.list': {},
'.retrieve': {},
'.create': {},
'.update': {},
'.delete': {},
}
},
'orders': {
'.collection': {},
'.actions': {
'.list': {},
'.retrieve': {},
'.create': {},
}
}
}
}
First of all, we specified .endpoint: {}
which indicates that a set
of collections follows after a prefix api/
.
.collection: {}
specifies that its parent node is a collection (
e.g. users is a collection). .actions
is a namespace predicate within
which we define which REST actions are allowed to be performed on the
collection.
Next, we need to define the underlying properties of the resources
that are included in these collections i.e. their property schema.
This is defined within the node ‘*’. Let’s begin with products
collection as a reference. A product is described by a key, a name,
a description, a stock and a price. To expose this information to
the REST API, we define something like the following which indicates
that the aforementioned properties are string, string, string,
integer and float respectively.
'products': {
'.collection': {},
'*': {
'key': {
'.string': {'max_length': 10}
},
'name': {
'.string': {},
},
'description': {
'.string': {},
},
'stock': {
'.integer': {},
},
'price': {
'.float': {},
},
},
'.actions': {
'.list': {},
'.retrieve': {},
}
}
This process can be repeated for all the collections of your application until you form the final specification. APIMAS provides a set of predicates which are used and understood from all the applications (which support APIMAS) to help you create your specification. Finally, we get something like this:
API_SPEC = {
'api': {
'.endpoint': {},
'users': {
'.collection': {},
'*': {
'id': {
'.serial': {},
},
'username': {
'.string': {},
'.required': {},
},
'first_name': {
'.string': {},
'required': {},
},
'last_name': {
'.string': {},
'.required': {},
},
'password': {
'.string': {},
'.required': {},
'.writeonly': {},
},
'email': {
'.email': {},
'.required': {},
},
},
'.actions': {
'.create': {},
'.update': {},
'.retrieve': {},
}
},
'products': {
'.collection': {},
'*': {
'key': {
'.string': {'max_length': 10}
},
'name': {
'.string': {},
},
'description': {
'.string': {},
},
'stock': {
'.integer': {},
},
'price': {
'.float': {},
},
},
'.actions': {
'.list': {},
'.retrieve': {},
}
},
'carts': {
'.collection': {},
'*': {
'customer': {
'.required': {},
'.ref': {'to': 'api/users'},
},
'ordered': {
'.boolean': {},
'.readonly': {},
},
'products': {
'.readonly': {},
'.structarray': {
'key': {
'.string': {},
},
'name': {
'.string': {},
},
'price': {
'.float': {},
},
}
},
},
'.actions': {
'.list': {},
'.retrieve': {},
'.create': {},
'.update': {},
'.delete': {},
},
},
'orders': {
'.collection': {},
'*': {
'id': {
'.serial': {},
'.readonly': {},
},
'address': {
'.required': {},
'.string': {},
},
'date': {
'.datetime': {'format': ['%Y-%m-%d %H:%M']},
'.required': {},
},
'cart': {
'.ref': {'to': 'api/carts'},
'.required': {},
}
},
'.actions': {
'.list': {},
'.create': {},
'.update': {},
'.delete': {},
'.retrieve': {},
}
},
}
}
Note
cart
field of collection orders
points to a resource of
another collection, i.e. carts
as specified in the ‘api/carts’
location of specification.
See also
For the full reference, see APIMAS predicates.