restlayer

package module
v0.0.0-...-ea8d11b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2016 License: MIT Imports: 0 Imported by: 0

README

REST Layer

Licenses

All source code is licensed under the MIT License.

Documentation

Overview

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/cool-rest/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/cool-rest/rest-layer-mongo), ElastiSearch (http://github.com/cool-rest/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/cool-rest/rest-layer/blob/master/README.md for full REST Layer documentation.

Example
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/cool-rest/xlog)
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.Append(xaccess.NewHandler())

// Add CORS support with passthrough option on so rest-layer can still
// handle OPTIONS method
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)
}
Output:

Directories

Path Synopsis
examples
Package graphql is a `net/http` handler implementing the GraphQL protocol for the REST Layer framework.
Package graphql is a `net/http` handler implementing the GraphQL protocol for the REST Layer framework.
Package resource defines and manages the resource graph and handle the interface with the resource storage handler.
Package resource defines and manages the resource graph and handle the interface with the resource storage handler.
Package rest is a `net/http` handler responsible for HTTP RESTful implementation for the REST Layer framework.
Package rest is a `net/http` handler responsible for HTTP RESTful implementation for the REST Layer framework.
Package schema provides a validation framework for the API resources.
Package schema provides a validation framework for the API resources.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL