rest-layer: github.com/rs/rest-layer Index | Examples | Files | Directories

package restlayer

import "github.com/rs/rest-layer"

Package restlayer is an API framework heavily inspired by the excellent Python Eve (http://python-eve.org/). It helps you create a comprehensive, customizable, and secure REST (graph) API on top of pluggable backend storages with no boiler plate code so can focus on your business logic.

Implemented as a net/http middleware, it plays well with other middleware like CORS (http://github.com/rs/cors) and is net/context aware thanks to xhandler.

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 to generate 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 (http://github.com/rs/rest-layer-mongo), ElastiSearch (http://github.com/rs/rest-layer-es) and other databases are also available so you have few to no code to write to make the whole system work.

Moreover, REST Layer let you create a graph API by linking resources between them. Thanks to its advanced field selection syntax (and coming support of GraphQL), you can gather resources and their dependencies in a single request, saving you from costly network roundtrips.

REST Layer is composed of several sub-packages:

- rest: Holds the `net/http` handler responsible for the implementation of the
  RESTful API.
- graphql: Holds a `net/http` handler to expose the API using the GraphQL protocol.
- schema: Provides a validation framework for the API resources.
- resource: Defines resources, manages the resource graph and manages the
  interface with resource storage handler.

See https://github.com/rs/rest-layer/blob/master/README.md for full REST Layer documentation.

Code:

var (
    // Define a user resource schema
    user = schema.Schema{
        Fields: schema.Fields{
            "id": {
                Required: true,
                // When a field is read-only, on default values or hooks can
                // set their value. The client can't change it.
                ReadOnly: true,
                // This is a field hook called when a new user is created.
                // The schema.NewID hook is a provided hook to generate a
                // unique id when no value is provided.
                OnInit: schema.NewID,
                // The Filterable and Sortable allows usage of filter and sort
                // on this field in requests.
                Filterable: true,
                Sortable:   true,
                Validator: &schema.String{
                    Regexp: "^[0-9a-f]{32}$",
                },
            },
            "created": {
                Required:   true,
                ReadOnly:   true,
                Filterable: true,
                Sortable:   true,
                OnInit:     schema.Now,
                Validator:  &schema.Time{},
            },
            "updated": {
                Required:   true,
                ReadOnly:   true,
                Filterable: true,
                Sortable:   true,
                OnInit:     schema.Now,
                // The OnUpdate hook is called when the item is edited. Here we use
                // provided Now hook which just return the current time.
                OnUpdate:  schema.Now,
                Validator: &schema.Time{},
            },
            // Define a name field as required with a string validator
            "name": {
                Required:   true,
                Filterable: true,
                Validator: &schema.String{
                    MaxLen: 150,
                },
            },
        },
    }

    // Define a post resource schema
    post = schema.Schema{
        Fields: schema.Fields{
            // schema.*Field are shortcuts for common fields (identical to users' same fields)
            "id":      schema.IDField,
            "created": schema.CreatedField,
            "updated": schema.UpdatedField,
            // Define a user field which references the user owning the post.
            // See bellow, the content of this field is enforced by the fact
            // that posts is a sub-resource of users.
            "user": {
                Required:   true,
                Filterable: true,
                Validator: &schema.Reference{
                    Path: "users",
                },
            },
            "public": {
                Filterable: true,
                Validator:  &schema.Bool{},
            },
            // Sub-documents are handled via a sub-schema
            "meta": {
                Schema: &schema.Schema{
                    Fields: schema.Fields{
                        "title": {
                            Required: true,
                            Validator: &schema.String{
                                MaxLen: 150,
                            },
                        },
                        "body": {
                            Validator: &schema.String{
                                MaxLen: 100000,
                            },
                        },
                    },
                },
            },
        },
    }
)

// Create a REST API root resource
index := resource.NewIndex()

// Add a resource on /users[/:user_id]
users := index.Bind("users", user, mem.NewHandler(), resource.Conf{
    // We allow all REST methods
    // (rest.ReadWrite is a shortcut for []rest.Mode{Create, Read, Update, Delete, List})
    AllowedModes: resource.ReadWrite,
})

// Bind a sub resource on /users/:user_id/posts[/:post_id]
// and reference the user on each post using the "user" field of the posts resource.
posts := users.Bind("posts", "user", post, mem.NewHandler(), resource.Conf{
    // Posts can only be read, created and deleted, not updated
    AllowedModes: []resource.Mode{resource.Read, resource.List, resource.Create, resource.Delete},
})

// Add a friendly alias to public posts
// (equivalent to /users/:user_id/posts?filter={"public":true})
posts.Alias("public", url.Values{"filter": []string{"{\"public\"=true}"}})

// Create API HTTP handler for the resource graph
api, err := rest.NewHandler(index)
if err != nil {
    log.Fatalf("Invalid API configuration: %s", err)
}

// Init an alice handler chain (use your preferred one)
c := alice.New()

// Add close notifier handler so context is cancelled when the client closes
// the connection
//c.Append(xhandler.CloseHandler)

// Add timeout handler
//c.Append(xhandler.TimeoutHandler(2 * time.Second))

// Install a logger (see https://github.com/rs/xlog)
c = c.Append(xlog.NewHandler(xlog.Config{}))
resource.LoggerLevel = resource.LogLevelDebug
resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
    xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
}

// Log API access
c = c.Append(xaccess.NewHandler())

// Add CORS support with passthrough option on so rest-layer can still
// handle OPTIONS method
c = c.Append(cors.New(cors.Options{OptionsPassthrough: true}).Handler)

// Bind the API under /api/ path
http.Handle("/api/", http.StripPrefix("/api/", c.Then(api)))

// Serve it
log.Print("Serving API on http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
    log.Fatal(err)
}

Index

Examples

Package Files

doc.go

Directories

PathSynopsis
graphqlPackage graphql is a `net/http` handler implementing the GraphQL protocol for the REST Layer framework.
resourcePackage resource defines and manages the resource graph and handle the interface with the resource storage handler.
restPackage rest is a `net/http` handler responsible for HTTP RESTful implementation for the REST Layer framework.
schemaPackage schema provides a validation framework for the API resources.
schema/encodingPackage encoding is the intended hierarchy location for encoding Schema to other formats.
schema/encoding/jsonschemaPackage jsonschema provides JSON Schema Draft 4 encoding support for schema.Schema.

Updated 2017-03-15. Refresh now. Tools for package owners.