Hello everyone !
(I first asked this on some stack-echanges but on every site I posted
this, I've been redirected by the community because it wasn't the
right place, I hope I'm not posting this in the wrong place this time
I'm an IT student and for my end-of-year project at school I had to
create a framework to serve a RESTful JSON API. I wasn't authorized to
use a project like Ruby on Rails for instance, Sinatra is allowed
since it does not provide any direct way to create a JSON RESTful API.
So I decided to use Sinatra as the base of this framework. While my
classmates are recreating a MVC framework (where view is the JSON) I
decided to follow another approach to create this framework. I started
from scratch, without any pre-compiled ideas about what philosphy my
framework should follow. With this in head I've ended up creating
something I called MEA : Models, Envelope, Actions.
What I'm asking :
- I would like to have some reviews about the concept I'm going to
descibe. Is it viable ? Is there some "stupid" ideas in it ?
What is MEA
I've realized the following when I was thinking about how I design a
JSON api (it might not be the best practices, though) :
- Always the same HTTP Routes.
- Over-using of before_filters etc... (to avoid code-redundancy most
of the time).
- Using JBuilder to "render the view".
Keeping this in mind, I've created this way to design a JSON api :
- HTTP Routes are hard-coded in the framework.
- Instead of using things like before_filters, a route should be able
to call a chain of N methods. => Methods are called "actions".
- It must be easy to describe the chain to the framework (like rails
*filters) for a custom dispatching system. => Called MEARoutes.
- A global read-write context should be passed between every methods
called in that chain. => Called Envelope.
- The models, like in any other frameworks, are a model that
represents a database-entity and give a wrapping around this entity to
manipulate it in its database.
In MEA the HTTP Routes are hard-coded and are described this way :
(defult sub_call: all)
(defult sub_call: one)
POST /:resource_name =>
(defult sub_call: create)
Expected body :
PUT /:resource_name/:id =>
(defult sub_call: update)
Expected body :
(defult sub_call: delete)
The `sub_call` is used to determine the MEARoute that will be used.
`data` holds the raw data (in JSON, like the attributes to change in a
model for the put call).
MEARoutes, envelope and actions
MEARoutes are used to decide the chain method that will be called by a
sub_call/resource couple call.
To make it easily readable, I decided to use YAML, here is a
declaration of a chain for a sub_call/resource call :
- User.check will check the currently logged user and will register
it into the envelope.
- Blogpost.retrieve will get the Blogpost to add the comment on and
add this blogpost to the envelope.
- Comment.create will create the entity representing the comment in
User.check and Blogpost.retrieve are reusable components that are
designed to add something into the envelope using the http request.
They can stop the chain process by simply returning an HTTP code and
some data if needed. A chain MUST, at least, return a code and an
empty object in the last action. The returned value will be JSON
serialized and returned to the client.
Comment.create would call a method to enforce some values in the
envelope like : `@envelope.must_have :user, :blogpost`. If it doesn't
have those values, then the chain must stop and return a 500 error
since it is the developer role to call the actions correctly.
If I want to do something like jbuilder I can create another action
that would be getting the needed entities from the envelope and
present the data in the way excepted for this API call.
The main advantage I see to this pattern is the testability of a simple action.
Here is a try implementation I've made to test the concept viability :
What do you think about all of this ? All remarks, even negative ones,
are welcome !