Fork me on GitHub

Revel

A high-productivity web framework for the Go language.

Hot Code Reload

Edit, save, and refresh. Revel compiles your code and templates for you, so you don't miss a beat. Code doesn't compile? It gives you a helpful description. Run-time code panic? Revel has you covered.

High Performance

Revel builds on top of the Go HTTP server, which was recently benchmarked to serve three to ten times as many requests as Rails across a variety of loads.

Synchronous

The Go HTTP server runs each request in its own goroutine. Write simple callback-free code without guilt.

Stateless

Revel provides primitives that keep the web tier stateless for predictable scaling. For example, you store session data in the user cookie, and cache data in a memcached cluster.

Simplicity Optional

Revel tries to provide a comprehensive toolkit for making everyday web apps. Don't want to use some of the helpers? Revel gets out of the way and gives you direct access to the underlying request and response.

Revel ships with sample applications to provide examples of real-world usage.

You will need a functioning Go 1.1 installation for this to work.

The commands at right will:

  1. Install Revel into your GOPATH
  2. Build the Revel command-line tool
  3. Run the Chat sample application
Once it's running, open a browser to http://localhost:9000/, and read how the chat demo is implemented.

  # From the base of your GOPATH...
  go get github.com/robfig/revel
  go build -o bin/revel github.com/robfig/revel/cmd
  bin/revel run github.com/robfig/revel/samples/chat
            

Development is closing in on the "final" 1.0 design, but the rate of change is still high. Expect to get your hands dirty.

Join our Google Group to take part in the design and development, or our announcement list to only be notified for new releases.

This section gives you a taste of various parts of the framework:

Routing
A simple declarative routing syntax
Controllers
Revel organizes endpoints into Controllers. They provide easy data binding and form validation.
Templates
Revel makes Go Templates simple to use at scale.
Interceptors
Register functionality to be called before or after actions. They can be activated per action, per Controller, or globally.
Plugins
More general functionality (e.g. transaction management) can be implemented with Plugins.

Routing

Revel uses a more powerful version of the original Play! routing syntax. It collects all routes for an app in a single file, with a simple syntax for matching requests, extracting arguments from URIs, and specifying route-specific arguments to the action. Here's a commented sample...

# conf/routes
# This file defines all application routes (Higher priority routes first)
GET    /login                 Application.Login       # A simple path
GET    /hotels/?              Hotels.Index            # Match /hotels and /hotels/ (optional trailing slash)
GET    /hotels/{id}           Hotels.Show             # Extract a URI argument (matching /[^/]+/)
POST   /hotels/{<[0-9]+>id}   Hotels.Save             # URI arg with custom regex
WS     /hotels/{id}/feed      Hotels.Feed             # WebSockets.
POST   /hotels/{id}/{action}  Hotels.{action}         # Automatically route some actions.
GET    /public/{<.*>filepath} Static.Serve("public")  # Map /app/public resources under /public/...
*      /{controller}/{action} {controller}.{action}   # Catch all; Automatic URL generation

Controllers

All Actions are methods on a Controller. This teaser shows a couple cool things:

  • Data binding. Revel binds simple values and structs from the URL or form and passes them as parameters to your method. (If you prefer to access them directly from a parameter map, that's ok too!)
  • Validation. Helpers to manage validation errors.
  • Flash. The flash is a cookie that lives for one request (errors, success messages, etc).
  • Session. The session is a cryptographically signed cookie, exposed as a map[string]string.
  • Results. Redirections take advantage of reverse routing. Template rendering makes your data available using the name of the local variable!
Here's an example:

// app/controllers/app.go

type Application struct {
	*revel.Controller
}

func (c Application) Register() revel.Result {
	title := "Register"
	return c.Render(title)
}

func (c Application) SaveUser(user models.User, verifyPassword string) revel.Result {
	c.Validation.Required(verifyPassword)
	c.Validation.Required(verifyPassword == user.Password)
		Message("Password does not match")
	user.Validate(c.Validation)

	if c.Validation.HasErrors() {
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(Application.Register)
	}

	user.HashedPassword, _ = bcrypt.GenerateFromPassword(
		[]byte(user.Password), bcrypt.DefaultCost)
	err := c.Txn.Insert(&user)
	if err != nil {
		panic(err)
	}

	c.Session["user"] = user.Username
	c.Flash.Success("Welcome, " + user.Name)
	return c.Redirect(Hotels.Index)
}
            

Templates

By convention, Revel manages to integrate Go Templates easily into the rest of the web app. Here is part of the template rendered in the Register action shown above.

Note that:
  • Revel found it automatically using the name of the action.
  • field is a simple helper function that returns a map of validation errors and parameter values for the named field. The app may inject any helper funcs that it wants.
  • The title variable is available in the template as if it had been explicitly put in the RenderArgs. (It's used in header.html in this case)
{{/* app/views/Application/Register.html */}}

{{template "header.html" .}}

<h1>Register:</h1>
<form action="/register" method="POST">
  {{with $field := field "user.Username" .}}
    <p class="{{$field.ErrorClass}}">
      <strong>Username:</strong>
      <input type="text" name="{{$field.Name}}" size="16" value="{{$field.Flash}}"> *
      <span class="error">{{$field.Error}}</span>
    </p>
  {{end}}

  {{/* other fields */}}

  <p class="buttons">
    <input type="submit" value="Register"> <a href="/">Cancel</a>
  </p>
</form>

{{template "footer.html" .}}

Interceptors

Interceptors are useful for checking pre-conditions (e.g. that the user is logged in) and for cleaning up afterwards.

// app/controllers/app.go

// This interceptor loads the user (using a helper).
// If it's nil, the user isn't logged in so it redirects them to the login page.
// Otherwise, it adds the user to the render context.
func checkLogin(c *revel.Controller) revel.Result {
	if user := connected(c); user != nil {
		c.RenderArgs["user"] = user
	} else {
		return c.Redirect(Application.Login)
	}
}

func init() {
	revel.InterceptFunc(checkLogin, revel.BEFORE, &Application{})
}

Plugins

Any class that meets the Plugin interface may be registered.

type Plugin interface {
	OnAppStart()
	OnRoutesLoaded(router *Router)
	BeforeRequest(c *Controller)
	AfterRequest(c *Controller)
	OnException(c *Controller, err interface{})
	Finally(c *Controller)
}

An empty implementation is provided; clients can embed that and then implement only the hook of interest. More hooks will be added as they are found useful.

For example, this is how you can use a plugin to manage a SQLite database.

// app/controllers/db.go

import (
	_ "github.com/mattn/go-sqlite3"
	"github.com/robfig/revel"
)

var (
	db *sql.DB
)

type DbPlugin struct {
	revel.EmptyPlugin
}

func (p DbPlugin) OnAppStart() {
	db, _ = sql.Open("sqlite3", ":memory:")

	// Create tables and insert some records.
	db.Exec(`
create table User (
  UserId         integer primary key autoincrement,
  Username       varchar(20),
  HashedPassword blob,
  Name           varchar(100))`)

	bcryptPassword, _ := bcrypt.GenerateFromPassword(
		[]byte("demo"), bcrypt.DefaultCost)
	db.Exec("insert into User (Username, HashedPassword, Name)" +
		" values ('demo', ?, 'Demo User')", bcryptPassword)
}

// Start a new transaction for each request.
func (p DbPlugin) BeforeRequest(c *revel.Controller) {
	txn, _ := db.Begin()
	c.Txn = txn
}

// Commit the transaction at the conclusion.
func (p DbPlugin) AfterRequest(c *revel.Controller) {
	c.Txn.Commit()
	c.Txn = nil
}

// Rollback in the case of exception.
func (p DbPlugin) OnException(c *revel.Controller, err interface{}) {
	c.Txn.Rollback()
}

func init() {
	revel.RegisterPlugin(DbPlugin{})
}
            

There are some areas that could benefit from some TLC.