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, session data is stored in the user cookie, and the cache is backed by a memcached cluster.

Modular

Revel is built around composable middleware called filters, which implement nearly all request-processing functionality. Developers have the freedom to replace the default filters with custom implementations (e.g. a custom router).

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.

  go get github.com/robfig/revel/revel
  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 in IRC at Freenode #revel. You may join 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. Type-safe reverse routing.
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 Controller.
Filters
More general functionality can be implemented with Filters.

Routing

Revel uses a declarative 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            # Matches with or without trailing slash
GET    /hotels/:id           Hotels.Show             # Extract an embedded argument
WS     /hotels/:id/feed      Hotels.Feed             # WebSockets.
POST   /hotels/:id/:action   Hotels.:action          # Automatically route some actions.
GET    /public/*filepath     Static.Serve("public")  # Assets served from /public/...
*      /:controller/:action  :controller.:action     # Catch all; Automatic URL generation

Reverse routes can be generated in a type-safe manner. For example:

// Show the hotel information.
func (c Hotels) Show(id int) revel.Result {
	hotel := HotelById(id)
	return c.Render(hotel)
}

// Save the updated hotel information and redirect back to Show.
func (c Hotels) Save(hotel Hotel) revel.Result {
	// validate and save hotel
	return c.Redirect(routes.Hotels.Show(hotel.Id))
}

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(routes.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(routes.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 controller methods that are run before or after requests, or in response to panics. By embedding a controller into another, a developer can share interceptors and fields across many controllers.

As an example, the database module may be used to open a connection on initialization, made available through a global handle. Additionally, embedding the db.Transactional type adds a sql.Txn field plus interceptors that begin and commit transactions (or rollback on panic).

Here's what the interceptor looks like (minus error handling):

// github.com/robfig/revel/modules/db/app/db.go

var Db *sql.DB

func Init() {
	// Read configuration.
	Driver, _ = revel.Config.String("db.driver")
	Spec, _ = revel.Config.String("db.spec")

	// Open a connection.
	Db, _ = sql.Open(Driver, Spec)
}

// Transactional adds transaction management to your controller.
type Transactional struct {
	*revel.Controller
	Txn *sql.Tx
}

func (c *Transactional) Begin() revel.Result {
	c.Txn, _ = Db.Begin()
	return nil
}

func (c *Transactional) Commit() revel.Result {
	_ = c.Txn.Commit()
	c.Txn = nil
	return nil
}

func (c *Transactional) Rollback() revel.Result {
	_ = c.Txn.Rollback()
	c.Txn = nil
	return nil
}

func init() {
	revel.InterceptMethod((*Transactional).Begin, revel.BEFORE)
	revel.InterceptMethod((*Transactional).Commit, revel.AFTER)
	revel.InterceptMethod((*Transactional).Rollback, revel.PANIC)
}

Here is how it can be mixed in to an application controller:

type Bookings struct {
	*revel.Controller
	db.Transactional  // Adds .Txn
	user.Login        // Adds .User
}

func (c Bookings) ShowFirstBooking() revel.Result {
	row := c.Txn.QueryRow(`
select id, hotel_id, user_id, price, nights
  from Booking
 where UserId = ?
 limit 1`, c.User.Id)
	...
	return c.Render(booking)
}

Filters

Filters are the middleware of the application. They are simply funcs with a specific signature:

type Filter func(c *Controller, filterChain []Filter)

Even complicated "built-in" functionality like the interceptor framework is implemented as a filter:

// github.com/robfig/revel/intercept.go

var InterceptorFilter = func(c *Controller, fc []Filter) {
	defer invokeInterceptors(FINALLY, c)
	defer func() {
		if err := recover(); err != nil {
			invokeInterceptors(PANIC, c)
			panic(err)
		}
	}()

	// Invoke the BEFORE interceptors and return early, if we get a result.
	invokeInterceptors(BEFORE, c)
	if c.Result != nil {
		return
	}

	fc[0](c, fc[1:])
	invokeInterceptors(AFTER, c)
}

Revel provides a default stack of Filters which the developer can override. This makes it easy for the developer to select exactly the parts of the framework that they want to use.

// github.com/robfig/revel/filter.go

// Filters is the default set of global filters.
// It may be set by the application on initialization.
var Filters = []Filter{
	PanicFilter,             // Recover from panics and display an error page instead.
	RouterFilter,            // Use the routing table to select the right Action
	FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
	ParamsFilter,            // Parse parameters into Controller.Params.
	SessionFilter,           // Restore and write the session cookie.
	FlashFilter,             // Restore and write the flash cookie.
	ValidationFilter,        // Restore kept validation errors and save new ones from cookie.
	I18nFilter,              // Resolve the requested language
	InterceptorFilter,       // Run interceptors around the action.
	ActionInvoker,           // Invoke the action.
}
						

Nearly all framework functionality is implemented in the filters, and the filter stack is directly exposed to the developer as part of the configuration. This makes Revel understandable and modular.

As proof of modularity, look how simple the main server handler is:

// github.com/robfig/revel/server.go

func handleInternal(w http.ResponseWriter, r *http.Request, ws *websocket.Conn) {
	var (
		req  = NewRequest(r)
		resp = NewResponse(w)
		c    = NewController(req, resp)
	)
	req.Websocket = ws

	Filters[0](c, Filters[1:])
	if c.Result != nil {
		c.Result.Apply(req, resp)
	}
}

There are some areas that could benefit from some TLC.