REST Layer, Go (golang) REST API framework
REST Layer is an API framework heavily inspired by the excellent Python Eve. It helps you create a comprehensive, customizable, and secure REST (graph) API on top of pluggable backend storages with no boiler plate code so you can focus on your business logic.
Implemented as a net/http handler, it plays well with standard middleware like CORS. It is also context aware. This allows deadline management to be supported down to the storage and permit an easy extensibility by passing custom data between layers of the framework.
REST Layer is an opinionated framework. Unlike many API frameworks, you don't directly control the routing and you don't have to write handlers. You just define resources and sub-resources with a schema, the framework automatically figures out what routes need to be generated behind the scene. You don't have to take care of the HTTP headers and response, JSON encoding, etc. either. REST layer handles HTTP conditional requests, caching, integrity checking for you.
A powerful and extensible validation engine make sure that data comes pre-validated to your custom storage handlers. Generic resource handlers for MongoDB, ElasticSearch and other databases are also available so you have few to no code to write to get up and running.
Moreover, REST Layer let you create a graph API by linking resources between them. Thanks to its advanced field selection syntax or GraphQL support, you can gather resources and their dependencies in a single request, saving you from costly network round-trips.
The REST Layer framework is composed of several sub-packages:
| Package | Coverage | Description |
|---|---|---|
| rest | A net/http handler to expose a REST-ful API. |
|
| graphql | A net/http handler to expose your API using the GraphQL protocol. |
|
| schema | A validation framework for the API resources. | |
| resource | Defines resources, manages the resource graph and manages the interface with resource storage handler. |
Until we reach a stable v1, there will be occasional breaking changes to the rest-layer APIs. Breaking changes will however not arrive at patch releases.
No breaking changes since v0.2.0.
Below is an incomplete list of breaking changes included in v0.2.0:
ValuesValidator FieldValidator attribute in schema.Dict struct replaced by Values Field.ValuesValidator FieldValidator attribute in schema.Array struct replaced by Values Field.Expression implementer in query.Predicate.filter parameters in sub-query will be validated for type match.filter parameters will be validated for type match only, instead of type & constrains.Reference projection fields will be validated against referenced resource schema.Connection projection fields will be validated against connected resource schema.OnUpdate field hook on HTTP PUT for existing documents. Deleting a field with Default value set, will always be reset to its default value.net/http middlewareAs REST Layer is a simple net/http handler. You can use standard middleware to extend its functionalities:
…
Just run this code (or use the provided examples/demo):
$ go run examples/demo/main.go
2015/07/27 20:54:55 Serving API on http://localhost:8080
Using HTTPie, you can now play with your API.
First create a user:
$ http POST :8080/api/users name="John Doe"
HTTP/1.1 201 Created
Content-Length: 155
Content-Location: /api/users/ar6ejgmkj5lfl98r67p0
Content-Type: application/json
Date: Mon, 27 Jul 2015 19:10:20 GMT
Etag: "1e18e148e1ff3ecdaae5ec03ac74e0e4"
Last-Modified: Mon, 27 Jul 2015 19:10:20 GMT
Vary: Origin
{
"id": "ar6ejgmkj5lfl98r67p0",
"created": "2015-07-27T21:10:20.671003126+02:00",
"updated": "2015-07-27T21:10:20.671003989+02:00",
"name": "John Doe",
}
As you can see, the id, created and updated fields have been automatically generated by our OnInit field hooks.
Also notice the Etag and Last-Modified headers. Those guys allow data integrity and concurrency control down to the storage layer through the use of the If-Match and If-Unmodified-Since headers. They can also serve for conditional requests using If-None-Match and If-Modified-Since headers.
Here is an example of conditional request:
$ http :8080/api/users/ar6ejgmkj5lfl98r67p0 \
If-Modified-Since:"Mon, 27 Jul 2015 19:10:20 GMT"
HTTP/1.1 304 Not Modified
Date: Mon, 27 Jul 2015 19:17:11 GMT
Vary: Origin
And here is a data integrity request following the RFC-5789 recommendations:
$ http PATCH :8080/api/users/ar6ejgmkj5lfl98r67p0 \
name="Someone Else" If-Match:invalid-etag
HTTP/1.1 412 Precondition Failed
Content-Length: 58
Content-Type: application/json
Date: Mon, 27 Jul 2015 19:33:27 GMT
Vary: Origin
{
"code": 412,
"fields": null,
"message": "Precondition Failed"
}
Retry with the valid etag:
$ http PATCH :8080/api/users/ar6ejgmkj5lfl98r67p0 \
name="Someone Else" If-Match:'"1e18e148e1ff3ecdaae5ec03ac74e0e4"'
HTTP/1.1 200 OK
Content-Length: 159
Content-Type: application/json
Date: Mon, 27 Jul 2015 19:36:19 GMT
Etag: "7bb7a71b0f66197aa07c4c8fc9564616"
Last-Modified: Mon, 27 Jul 2015 19:36:19 GMT
Vary: Origin
{
"created": "2015-07-27T21:33:09.168492448+02:00",
"id": "ar6ejmukj5lflde9q8bg",
"name": "Someone Else",
"updated": "2015-07-27T21:36:19.904545093+02:00"
}
Note that even if you don't use conditional request, the Etag is always used by the storage handler to manage concurrency control between requests.
Another cool thing is sub-resources. We've set our posts resource as a child of the users resource. This way we can handle ownership very easily as routes are constructed as /users/:user_id/posts.
Lets create a post:
$ http POST :8080/api/users/ar6ejgmkj5lfl98r67p0/posts \
title="My first post"
HTTP/1.1 200 OK
Content-Length: 212
Content-Type: application/json
Date: Mon, 27 Jul 2015 19:46:55 GMT
Etag: "307ae92df6c3dd54847bfc7d72422e07"
Last-Modified: Mon, 27 Jul 2015 19:46:55 GMT
Vary: Origin
{
"id": "ar6ejs6kj5lflgc28es0",
"created": "2015-07-27T21:46:55.355857401+02:00",
"updated": "2015-07-27T21:46:55.355857989+02:00",
"title": "My first post",
"user": "ar6ejgmkj5lfl98r67p0"
}
Notice how the user field has been set with the user id provided in the route, that's pretty cool, huh?
We defined that we can cr
No open issues yet, or sync has not completed.