A Community driven Web framework written in Go. Seems to be the fastest. Simplicity equals productivity.
What are you waiting, start using Iris Web Framework today. Easy to learn, provides robust set of features for building modern & shiny web applications.
- FastHTTP: Iris is builded on top of the fasthttp
- Streaming: You have only one option when streaming comes in game*
- Sessions: Gorilla Sessions modified to work with Iris*
- Websockets: Gorilla Websockets modified to work with Iris*
- Context: Iris uses Context for storing route params, sharing variables between middlewares and render rich content to the client
- Plugins: You can build your own plugins to inject the Iris framework*
- Full API: All http methods are supported, you can group routes and sharing resources together*
- Zero allocations: Iris generates zero garbage
- Multi server instances: Besides the fact that Iris has a default main server. You can declare as many as you need*
- Middlewares: Create and use global or per route middlewares with the Iris' simplicity*.
Q: What makes iris significantly faster?
- First of all Iris is builded on top of the fasthttp
- Iris uses the same algorithm as the BSD's kernel does for routing (call it Trie)
- Iris can detect what features are used and what don't and optimized itself before server run.
- Middlewares and Plugins are 'light' , that's a principle.
- Install
- Introduction
- TLS
- Handlers
- Using Handlers
- Using HandlerFuncs
- Using Annotated
- Using native http.Handler - Using native http.Handler via iris.ToHandlerFunc()
- Middlewares
- API
- Declaration & Options
- Party
- Named Parameters
- Catch all and Static serving
- Custom HTTP Errors
- Streaming
- Graceful
- Context
- Plugins
- Internationalization and Localization
- Examples
- Benchmarks
- Third Party Middlewares
- Contributors
- Community
- Todo
- External source articles
- License
In order to have the latest version update the package once per week
$ rm -rf $GOPATH/github.com/kataras/iris
$ go get github.com/kataras/irisThe name of this framework came from Greek mythology, Iris was the name of the Greek goddess of the rainbow. Iris is a very minimal but flexible golang http middleware & standalone web application framework, providing a robust set of features for building single & multi-page, web applications.
package main
import "github.com/kataras/iris"
func main() {
iris.Get("/hello", func(c *iris.Context) {
c.HTML("<b> Hello </b>")
})
iris.Listen(":8080")
}Note: for macOS, If you are having problems on .Listen then pass only the port "8080" without ':'
//1 defaults to tcp 8080
iris.Listen()
//2
log.Fatal(iris.Listen(":8080"))TLS for https://
ListenTLS(fulladdr string, certFile, keyFile string) error//1
iris.ListenTLS(":8080", "myCERTfile.cert", "myKEYfile.key")
//2
log.Fatal(iris.ListenTLS(":8080", "myCERTfile.cert", "myKEYfile.key"))Handlers should implement the Handler interface:
type Handler interface {
Serve(*Context)
}type myHandlerGet struct {
}
func (m myHandlerGet) Serve(c *iris.Context) {
c.Write("From %s", c.PathString())
}
//and so on
iris.Handle("GET", "/get", myHandlerGet)
iris.Handle("POST", "/post", post)
iris.Handle("PUT", "/put", put)
iris.Handle("DELETE", "/delete", del)HandlerFuncs should implement the Serve(*Context) func. HandlerFunc is most simple method to register a route or a middleware, but under the hoods it's acts like a Handler. It's implements the Handler interface as well:
type HandlerFunc func(*Context)
func (h HandlerFunc) Serve(c *Context) {
h(c)
}HandlerFuncs shoud have this function signature:
func handlerFunc(c *iris.Context) {
c.Write("Hello")
}
iris.HandleFunc("GET","/letsgetit",handlerFunc)
//OR
iris.Get("/get", handlerFunc)
iris.Post("/post", handlerFunc)
iris.Put("/put", handlerFunc)
iris.Delete("/delete", handlerFunc)Implements the Handler interface
///file: userhandler.go
import "github.com/kataras/iris"
type UserHandler struct {
iris.Handler `get:"/profile/user/:userId"`
}
func (u *UserHandler) Serve(c *iris.Context) {
userId := c.Param("userId")
c.RenderFile("user.html", struct{ Message string }{Message: "Hello User with ID: " + userId})
}///file: main.go
//...cache the html files, if you the content of any html file changed, the templates are auto-reloading
iris.Templates("src/iristests/templates/*.html")
//...register the handler
iris.HandleAnnotated(&UserHandler{})
//...continue writing your wonderful APINote that using native http handler you cannot access url params.
type nativehandler struct {}
func (_ nativehandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
}
func main() {
iris.Handle("", "/path", iris.ToHandler(nativehandler{}))
//"" means ANY(GET,POST,PUT,DELETE and so on)
}
iris.Get("/letsget", iris.ToHandlerFunc(nativehandler{}))
iris.Post("/letspost", iris.ToHandlerFunc(nativehandler{}))
iris.Put("/letsput", iris.ToHandlerFunc(nativehandler{}))
iris.Delete("/letsdelete", iris.ToHandlerFunc(nativehandler{}))Middlewares in Iris are not complicated, imagine them as simple Handlers. They should implement the Handler interface as well:
type Handler interface {
Serve(*Context)
}
type Middleware []HandlerHandler middleware example:
type myMiddleware struct {}
func (m *myMiddleware) Serve(c *iris.Context){
shouldContinueToTheNextHandler := true
if shouldContinueToTheNextHandler {
c.Next()
}else{
c.WriteText(403,"Forbidden !!")
}
}
iris.Use(&myMiddleware{})
iris.Get("/home", func (c *iris.Context){
c.HTML("<h1>Hello from /home </h1>")
})
iris.Listen()HandlerFunc middleware example:
func myMiddleware(c *iris.Context){
c.Next()
}
iris.UseFunc(myMiddleware)HandlerFunc middleware for a specific route:
func mySecondMiddleware(c *iris.Context){
c.Next()
}
iris.Get("/dashboard", func(c *iris.Context) {
loggedIn := true
if loggedIn {
c.Next()
}
}, mySecondMiddleware, func (c *iris.Context){
c.Write("The last HandlerFunc is the main handler, all before that are the middlewares for this route /dashboard")
})
iris.Listen(":8080")Uses one of build'n Iris middlewares, view practical examples here
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/logger"
)
type Page struct {
Title string
}
iris.Templates("./yourpath/templates/*")
iris.Use(logger.Logger())
iris.Get("/", func(c *iris.Context) {
c.RenderFile("index.html", Page{"My Index Title"})
})
iris.Listen(":8080") // .Listen() listens to TCP port 8080 by defaultUse of GET, POST, PUT, DELETE, HEAD, PATCH & OPTIONS
package main
import "github.com/kataras/iris"
func main() {
iris.Get("/home", testGet)
iris.Post("/login",testPost)
iris.Put("/add",testPut)
iris.Delete("/remove",testDelete)
iris.Head("/testHead",testHead)
iris.Patch("/testPatch",testPatch)
iris.Options("/testOptions",testOptions)
iris.Listen(":8080")
}
func testGet(c *iris.Context) {
//...
}
func testPost(c *iris.Context) {
//...
}
//and so on....Let's make a pause,
- Q: Other frameworks needs more lines to start a server, why Iris is different?
- A: Iris gives you the freedom to choose between three methods/ways to use Iris
- global iris.
- set a new iris with variable = iris**.New()**
- set a new iris with custom options with variable = iris**.Custom(options)**
import "github.com/kataras/iris"
// 1.
func methodFirst() {
iris.Get("/home",func(c *iris.Context){})
iris.Listen(":8080")
//iris.ListenTLS(":8080","yourcertfile.cert","yourkeyfile.key"
}
// 2.
func methodSecond() {
api := iris.New()
api.Get("/home",func(c *iris.Context){})
api.Listen(":8080")
}
// 3.
func methodThree() {
//these are the default options' values
options := iris.StationOptions{
Log: true,
Profile: false,
ProfilePath: iris.DefaultProfilePath,
PathCorrection: true, //explanation at the end of this chapter
}//these are the default values that you can change
//DefaultProfilePath = "/debug/pprof"
api := iris.Custom(options)
api.Get("/home",func(c *iris.Context){})
api.Listen(":8080")
}Note that with 2. & 3. you can define and use more than one Iris container in the same app, when it's necessary.
As you can see there are some options that you can chage at your iris declaration, you cannot change them after. If an option value not passed then it considers to be false if bool or the default if string.
For example if we do that...
import "github.com/kataras/iris"
func main() {
options := iris.StationOptions{
Profile: true,
ProfilePath: "/mypath/debug",
}
api := iris.Custom(options)
api.Listen(":8080")
}run it, then you can open your browser, type 'localhost:8080/mypath/debug/profile' at the location input field and you should see a webpage shows you informations about CPU.
For profiling & debug there are seven (7) generated pages ('/debug/pprof/' is the default profile path, which on previous example we changed it to '/mypath/debug'):
- /debug/pprof/cmdline
- /debug/pprof/profile
- /debug/pprof/symbol
- /debug/pprof/goroutine
- /debug/pprof/heap
- /debug/pprof/threadcreate
- /debug/pprof/pprof/block
PathCorrection corrects and redirects the requested path to the registed path for example, if /home/ path is requested but no handler for this Route found, then the Router checks if /home handler exists, if yes, redirects the client to the correct path /home and VICE - VERSA if /home/ is registed but /home is requested then it redirects to /home/ (Default is true)
Let's party with Iris web framework!
func main() {
//log everything middleware
iris.UseFunc(func(c *iris.Context) {
println("[Global log] the requested url path is: ", c.PathString())
c.Next()
})
// manage all /users
users := iris.Party("/users")
{
// provide a middleware
users.UseFunc(func(c *iris.Context) {
println("LOG [/users...] This is the middleware for: ", c.PathString())
c.Next()
})
users.Post("/login", loginHandler)
users.Get("/:userId", singleUserHandler)
users.Delete("/:userId", userAccountRemoveUserHandler)
}
// Party inside an existing Party example:
beta:= iris.Party("/beta")
admin := beta.Party("/admin")
{
/// GET: /beta/admin/
admin.Get("/", func(c *iris.Context){})
/// POST: /beta/admin/signin
admin.Post("/signin", func(c *iris.Context){})
/// GET: /beta/admin/dashboard
admin.Get("/dashboard", func(c *iris.Context){})
/// PUT: /beta/admin/users/add
admin.Put("/users/add", func(c *iris.Context){})
}
iris.Listen(":8080")
}Named parameters are just custom paths to your routes, you can access them for each request using context's c.Param("nameoftheparameter"). Get all, as array ({Key,Value}) using c.Params property.
No limit on how long a path can be.
Usage:
package main
import "github.com/kataras/iris"
func main() {
// MATCH to /hello/anywordhere (if PathCorrection:true match also /hello/anywordhere/)
// NOT match to /hello or /hello/ or /hello/anywordhere/something
iris.Get("/hello/:name", func(c *iris.Context) {
name := c.Param("name")
c.Write("Hello %s", name)
})
// MATCH to /profile/iris/friends/42 (if PathCorrection:true matches also /profile/iris/friends/42/ ,otherwise not match)
// NOT match to /profile/ , /profile/something ,
// NOT match to /profile/something/friends, /profile/something/friends ,
// NOT match to /profile/anything/friends/42/something
iris.Get("/profile/:fullname/friends/:friendId",
func(c *iris.Context){
name:= c.Param("fullname")
//friendId := c.ParamInt("friendId")
c.HTML("<b> Hello </b>"+name)
})
iris.Listen(":8080")
}####Catch all
// Will match any request which url's preffix is "/anything/" and has content after that
iris.Get("/anything/*randomName", func(c *iris.Context) { } )
// Match: /anything/whateverhere/whateveragain , /anything/blablabla
// c.Params("randomName") will be /whateverhere/whateveragain, blablabla
// Not Match: /anything , /anything/ , /somethingiris.Static("/public", "./static/assets/", 1))
//-> /public/assets/favicon.icoYou can define your own handlers for http errors, which can render an html file for example. e.g for for 404 not found:
package main
import "github.com/kataras/iris"
func main() {
iris.OnError(404,func (c *iris.Context){
c.HTML("<h1> The page you looking doesn't exists </h1>")
c.SetStatusCode(404)
})
//or OnNotFound(func (c *iris.Context){})... for 404 only.
//or OnPanic(func (c *iris.Context){})... for 500 only.
//...
}We saw how to declare a custom error for a http status code, now let's look for how to send/emit an error to the client manually , for example let's emit the 404 we defined before, simple:
iris.Get("/thenotfound",func (c *iris.Context) {
c.EmitError(404)
//or c.NotFound() for 404 only.
//and c.Panic() for 500 only.
})Fasthttp has very good support for doing progressive rendering via multiple flushes, streaming. Here is an example, taken from here
package main
import(
"github.com/kataras/iris"
"bufio"
"time"
"fmt"
)
func main() {
iris.Any("/stream",func (ctx *iris.Context){
ctx.Stream(stream)
})
iris.Listen()
}
func stream(w *bufio.Writer) {
for i := 0; i < 10; i++ {
fmt.Fprintf(w, "this is a message number %d", i)
// Do not forget flushing streamed data to the client.
if err := w.Flush(); err != nil {
return
}
time.Sleep(time.Second)
}
}Graceful package is not part of the Iris, it's not a Middleware neither a Plugin, so a new repository created, which it's a fork of https://github.com/tylerb/graceful.
How to use:
package main
import (
"time"
"github.com/iris-contrib/graceful"
"github.com/kataras/iris"
)
func main() {
api := iris.New()
api.Get("/", func(c *iris.Context) {
c.Write("Welcome to the home page!")
})
graceful.Run(":3001", time.Duration(10)*time.Second, api)
}
Inside the examples branch you will find practical examples
Plugins are modules that you can build to inject the Iris' flow. Think it like a middleware for the Iris framework itself, not only the requests. Middleware starts it's actions after the server listen, Plugin on the other hand starts working when you registed them, from the begin, to the end. Look how it's interface looks:
type (
// IPlugin is the interface which all Plugins must implement.
//
// A Plugin can register other plugins also from it's Activate state
IPlugin interface {
// GetName has to returns the name of the plugin, a name is unique
// name has to be not dependent from other methods of the plugin,
// because it is being called even before the Activate
GetName() string
// GetDescription has to returns the description of what the plugins is used for
GetDescription() string
// Activate called BEFORE the plugin being added to the plugins list,
// if Activate returns none nil error then the plugin is not being added to the list
// it is being called only one time
//
// PluginContainer parameter used to add other plugins if that's necessary by the plugin
Activate(IPluginContainer) error
}
IPluginPreHandle interface {
// PreHandle it's being called every time BEFORE a Route is registed to the Router
//
// parameter is the Route
PreHandle(IRoute)
}
IPluginPostHandle interface {
// PostHandle it's being called every time AFTER a Route successfully registed to the Router
//
// parameter is the Route
PostHandle(IRoute)
}
IPluginPreListen interface {
// PreListen it's being called only one time, BEFORE the Server is started (if .Listen called)
// is used to do work at the time all other things are ready to go
// parameter is the station
PreListen(*Station)
}
IPluginPostListen interface {
// PostListen it's being called only one time, AFTER the Server is started (if .Listen called)
// parameter is the station
PostListen(*Station)
}
IPluginPreClose interface {
// PreClose it's being called only one time, BEFORE the Iris .Close method
// any plugin cleanup/clear memory happens here
//
// The plugin is deactivated after this state
PreClose(*Station)
}
)A small example, imagine that you want to get all routes registered to your server (OR modify them at runtime), with their time registed, methods, (sub)domain and the path, what whould you do on other frameworks when you want something from the framework which it doesn't supports out of the box? and what you can do with Iris:
//file myplugin.go
package main
import (
"time"
"github.com/kataras/iris"
)
type RouteInfo struct {
Method string
Domain string
Path string
TimeRegisted time.Time
}
type myPlugin struct {
container iris.IPluginContainer
routes []RouteInfo
}
func NewMyPlugin() *myPlugin {
return &myPlugin{routes: make([]RouteInfo, 0)}
}
// All plugins must at least implements these 3 functions
func (i *myPlugin) Activate(container iris.IPluginContainer) error {
// use the container if you want to register other plugins to the server, yes it's possible a plugin can registers other plugins too.
// here we set the container in order to use it's printf later at the PostListen.
i.container = container
return nil
}
func (i myPlugin) GetName() string {
return "MyPlugin"
}
func (i myPlugin) GetDescription() string {
return "My Plugin is just a simple Iris plugin.\n"
}
//
// Implement our plugin, you can view your inject points - listeners on the /kataras/iris/plugin.go too.
//
// Implement the PostHandle, because this is what we need now, we need to add a listener after a route is registed to our server so we do:
func (i *myPlugin) PostHandle(route iris.IRoute) {
myRouteInfo := &RouteInfo{}
myRouteInfo.Method = route.GetMethod()
myRouteInfo.Domain = route.GetDomain()
myRouteInfo.Path = route.GetPath()
myRouteInfo.TimeRegisted = time.Now()
i.routes = append(i.routes, myRouteInfo)
}
// PostListen called after the server is started, here you can do a lot of staff
// you have the right to access the whole iris' Station also, here you can add more routes and do anything you want, for example start a second server too, an admin web interface!
// for example let's print to the server's stdout the routes we collected...
func (i *myPlugin) PostListen(s *iris.Station) {
i.container.Printf("From MyPlugin: You have registed %d routes ", len(i.routes))
//do what ever you want, if you have imagination you can do a lot
}
//Let's register our plugin:
//file main.go
package main
import "github.com/kataras/iris"
func main() {
iris.Plugin(NewMyPlugin())
//the plugin is running and caching all these routes
iris.Get("/", func(c *iris.Context){})
iris.Post("/login", func(c *iris.Context){})
iris.Get("/login", func(c *iris.Context){})
iris.Get("/something", func(c *iris.Context){})
iris.Listen()
}
Output:
From MyPlugin: You have registed 4 routes
An example of one plugin which is under development is the Iris control, a web interface that gives you control to your server remotely. You can find it's code here
Benchmarks results taken from external source, created by @smallnest.
This is the most realistic benchmark suite than you will find for Go Web Frameworks. Give attention to it's readme.md.
April 13 2016
click here to see all benchmarks
Note: After v1.1 most of the third party middlewares are incompatible, I'm putting a big effort to convert all of these to work with Iris, if you want to help please do so (pr).
| Middleware | Author | Description | Tested |
|---|---|---|---|
| sessions | Ported to Iris | Session Management | Yes |
| Graceful | Ported to iris | Graceful HTTP Shutdown | Yes |
| gzip | Iris | GZIP response compression | Yes |
| RestGate | Prasanga Siripala | Secure authentication for REST API endpoints | No |
| secure | Ported to Iris | Middleware that implements a few quick security wins | Yes |
| JWT Middleware | Auth0 | Middleware checks for a JWT on the Authorization header on incoming requests and decodes it |
No |
| binding | Matt Holt | Data binding from HTTP requests into structs | No |
| i18n | Iris | Internationalization and Localization | Yes |
| logrus | Dan Buch | Logrus-based logger | No |
| render | Cory Jacobsen | Render JSON, XML and HTML templates | No |
| gorelic | Jingwen Owen Ou | New Relic agent for Go runtime | No |
| pongo2 | Iris | Middleware for pongo2 templates | Yes |
| oauth2 | David Bochenski | oAuth2 middleware | No |
| permissions2 | Alexander Rødseth | Cookies, users and permissions | No |
| onthefly | Alexander Rødseth | Generate TinySVG, HTML and CSS on the fly | No |
| cors | Keuller Magalhaes | Cross Origin Resource Sharing (CORS) support | Yes |
| xrequestid | Andrea Franz | Middleware that assigns a random X-Request-Id header to each request | No |
| VanGoH | Taylor Wrobel | Configurable AWS-Style HMAC authentication middleware | No |
| stats | Florent Messa | Store information about your web application (response time, etc.) | No |
Thanks goes to the people who have contributed code to this package, see the GitHub Contributors page.
If you'd like to discuss this package, or ask questions about it, feel free to
- Provide a lighter, with less using bytes, to save middleware for a route.
- Create examples.
- Subdomains supports with the same syntax as iris.Get, iris.Post ...
- Provide a more detailed benchmark table
- Convert useful middlewares out there into Iris middlewares, or contact with their authors to do so.
- Provide automatic HTTPS using https://letsencrypt.org/how-it-works/.
- Create administration web interface as plugin.
- Create an easy websocket api.
- Create a mechanism that scan for Typescript files, compile them on server startup and serve them.
According to my article ( comparative ultra wide frame Go Http routing performance ) on a variety of relatively Go http routing framework, Iris clear winner, its performance far exceeds other Golang http routing framework.
- +1 pending article waiting, after writer's holidays
This project is licensed under the BSD 3-Clause License. License can be found here.




