Introduction to Writing Web Apps in Golang
Full stack developer @ RainingClouds
- Golang => Web Apps, System Apps
- Android => System Framework, Apps
- Objective C => iOS
- Scala => Play
- Java => Netty, Play
- Angular JS => Web app front end
- Python => Django, Flask
- HTML , Actionscript => Unfortunate
Why Go
Go web API:
- 20,000 samples
- 0 errors
- Average: 8ms
- Median: 5ms
- 90%: 20ms
- Max: 90ms
- Min: 1ms
-
Throughput:
1202.2 requests/s4691.79 requests/s (see update) - Memory usage: 10MBs
Rails (Rails 4, Ruby 2.0, production mode, puma web server):
- 20,000 samples
- 0 errors
- Average: 187ms
- Median: 163ms
- 90%: 214ms
- Min: 9ms
- Max: 178,552ms (442ms most of the time but big spike around 15,000 requests)
- Throughput: 88.9 requests/s
Memory usage: 117MBs
Benchmarks by Matt Aimonetti
Go
- Compiled language
- Statically typed
- Concurrent
- Garbage-collected
- High speed compilation
- Highly portable (including Andorid and soon on iOS as well)
Robert Griesemer, Ken Thompson, and Rob Pike
Web Apps
- Tools
- Framework
- Dependency Management
- DB Supports
- ORM
- Migration Supports
- JSON Support
- Go routines
- Live reloads
- Deployment
- Error logging
- Libraries
- Community
Tools
Go 1.4
SublimeText 3
+
GoSublime
Postgresapp
Mongohub
Development Tools
Web Frameworks
- Beego - beego is an open-source, high-performance web framework for the Go programming language.
- Bone - Lightning Fast HTTP Multiplexer.
- Gin - Gin is a web framework written in Go! It features a martini-like API with much better performance, up to 40 times faster. If you need performance and good productivity.
- Goat - A minimalistic REST API server in Go
- goose - Server Sent Events in Go
- gocraft/web - A mux and middleware package in Go.
22 in Total
Web Frameworks
Web Frameworks
- Golang philosophy believes in library format
- So usually (debatable) don't go for frameworks like revel (~Rails) which is convention based
- Try to avoid reflection as much as possible (revel, martini are heavily using refections)
- I go for Gin (slightly modified though)
Web Frameworks
Gin(https://github.com/gin-gonic/gin)
func getPort() string {
var port = os.Getenv("PORT")
// Set a default port if there is nothing in the environment
if port == "" {
port = "4747"
}
return ":" + port
}
func main() {
engine := gin.Default()
gin.SetMode(gin.ReleaseMode)
// pushing routes
pushRoutes(engine)
log.Println("Starting server", getPort())
// start http server
engine.Run(getPort())
}
Web Frameworks
Gin(https://github.com/gin-gonic/gin)
func UserLogin(c *gin.Context) {
c.AddHeader("Access-Control-Allow-Origin", "*")
var bodyMap map[string]interface{}
c.Bind(&bodyMap)
log.Println(bodyMap)
user, err := models.AuthenticateUser
(bodyMap["email"].(string), bodyMap["password"].(string))
if err != nil {
c.JSON(http.StatusUnauthorized,
gin.H{"success": false, "message": "Email/Password is wrong"})
return
}
if !user.IsActivated {
c.JSON(http.StatusUnauthorized,
gin.H{"success": false,
"message": "User is not activated yet !!\nPlease check your Email"})
return
}
c.JSON(http.StatusOK,
gin.H{"success": true, "session_key": user.SessionKey, "user": user})
}
Dependency Management
Godep (https://github.com/tools/godep)
Command godep helps build packages reproducibly by fixing their dependencies.
// to save your current dependecies
godep save
// on updating dependecies
godep update
// to restore the dependecies
godep restore
Highly recommended as Golang libraries are cutting edge and some of the changes might break your webapp, so always remain faithful to library versions unless you are sure about not getting caught naked !!
DB Support
Relational
NoSql
DB Support : Relational
- go-adodb - Microsoft ActiveX Object DataBase driver for go that using database/sql.
- go-mssqldb - Microsoft MSSQL driver prototype in go language.
- go-oci8 - Oracle driver for go that using database/sql.
- go-pgsql - A PostgreSQL client package for the Go Programming Language.
- go-sql-driver/mysql - MySQL driver for Go.
- go-sqlite3 - SQLite3 driver for go that using database/sql.
- pq - Pure Go Postgres driver for database/sql.
DB Support : NoSQL
- aerospike-client-go - Aerospike client in Go language.
- cayley - A graph database with support for multiple backends.
- go-couchbase - Couchbase client in Go
- gorethink - Go language driver for RethinkDB
- gomemcache - memcache client library for the Go programming language.
- mgo - MongoDB driver for the Go language that implements a rich and well tested selection of features under a very simple API following standard Go idioms.
- neo4j - Neo4j Rest API Bindings for Golang
- Neo4j-GO - Neo4j REST Client in golang.
- redigo - Redigo is a Go client for the Redis database.
- redis - A simple, powerful Redis client for Go.
DB Support
Don't confuse it with ORM, and don't worry Golang has plenty of ORM solutions
Golang also has drivers for elastic search
ORM
Object-relational mapping (ORM, O/RM, and O/R mapping) in computer science is a programming technique for converting data between incompatible type systems in object-oriented programming languages. This creates, in effect, a "virtual object database" that can be used from within the programming language.
Its crap
ORM means I don't have to write SQL :D
ORM
- BeeDB - go ORM,support database/sql interface,pq/mysql/sqlite.
- GORM - The fantastic ORM library for Golang, aims to be developer friendly.
- gorp - Go Relational Persistence, ORM-ish library for Go.
- hood - Database agnostic ORM for Go.
- QBS - Stands for Query By Struct. A Go ORM.
- upper.io/db - Single interface for interacting with different data sources through the use of adapters that wrap mature database drivers.
- Xorm - Simple and powerful ORM for Go.
ORM
If you consider features of GORM wins single handedly. I have used it for almost 3 projects, and it really really works
// get apk with id
err := db.DB().Model(Apk{}).Where("id = ?", app.BuildId).Find(&apk).Error
// get list of apps for account with accountId and in descending order of updated_at
err := db.Where(App{AccountId: accountId}).Order("updated_at desc").Find(&apps).Error
// find IPhoneDeviceRegistration for device id and app id
err := db.DB().Model(IPhoneDeviceRegistration{}).Where("device_id = ? and app_id = ?"
,deviceId, appId).Find(®istration).Error
Migration Support
func PerformMigrations(db *gorm.DB) {
log.Println("Running migrations")
initial("init", db)
second("removed gcm_id and app_id from device table
and added new registration entry", db)
third("added apk_version_name column so as to track the live exceptions", db)
fourth("added logcat to the exception", db)
fifth("adding new ipa and ios device", db)
sixth("added device name in ios device struct", db)
seventh("added iphone device registration struct", db)
}
func initial(name string, db *gorm.DB) {
// this is obvious
db.AutoMigrate(&models.Migration{})
if models.IsMigrationApplied(name) {
log.Println("Migration", name, "is already applied")
return
}
log.Println("Applying", name)
db.DropTable(&models.Account{})
db.AutoMigrate(&models.Account{})
db.DropTable(&models.User{})
db.AutoMigrate(&models.User{})
db.DropTable(&models.App{})
db.AutoMigrate(&models.App{})
db.DropTable(&models.Apk{})
db.AutoMigrate(&models.Apk{})
db.DropTable(&models.AndroidDevice{})
db.AutoMigrate(&models.AndroidDevice{})
db.DropTable(&models.InviteRequest{})
db.AutoMigrate(&models.InviteRequest{})
db.DropTable(&models.AndroidException{})
db.AutoMigrate(&models.AndroidException{})
err := models.MigrationApplied(name)
if err != nil {
panic(err)
}
log.Println("Migration", name, "is applied")
}
func second(name string, db *gorm.DB) {
Depends upon how do you want migrations to work
JSON Support
type User struct {
Id int `json:"user_id"`
AuthKey string `json:"-" sql:"not null;unique"`
IsActivated bool `json:"is_activated"`
IsSessionKeyStale bool `json:"-"`
SessionKey string `json:"-"`
Name string `json:"user_name" sql:"not null;"`
EmailAddress string `json:"email_address" sql:"not null;"`
Password string `json:"-" sql:"not null"`
AccountId int `json:"account_id" sql:"not null;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt time.Time `json:"deleted_at"`
}
Go has inbuilt JSON support (RFC 4627). (encoding/json)
Go Routines
....
app.BuildId = ipa.Id
err = app.Save()
if err != nil {
handleError(c, err)
return
}
// sending push notifications to all the devices if GCM id is available
go func(app *models.App) {
deviceRegistrations, err := models.GetDeviceRegistrationsForAppId(app.Id)
if err != nil {
log.Println(err.Error())
return
}
payload := make(map[string]interface{})
payload["make"] = "dist-1234"
payload["path"] = fmt.Sprintf("/app/%d/ipa", app.Id)
payload["name"] = app.AppName
payload["ver"] = app.Ipa.BundleVersion
for _, deviceRegistration := range deviceRegistrations {
gcm.SendMessage(1, deviceRegistration.GcmId,
"<id>", payload)
}
}(app)
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Apk is uploaded succesfully"})
The most simple asynchronous task APIs
Live Reload
# akshay at Akshays-MacBook-Pro.local in ~/CodeBase/distribute/src/distribute on git:master x [12:23:33]
$ gin run
[gin] listening on port 3000
[gin] ERROR! Build failed.
# distribute/controllers
controllers/appController.go:312: syntax error: nested func not allowed
[gin] Build Successful
Gin (not the framework!) (https://github.com/codegangsta/gin)
Deployment
Error logging
Almost any service that
has REST API
BugSnag
AirBrake
Sentry
Libraries
Golang is awesome, and there is awesome-go
https://github.com/avelino/awesome-go
Community
- Github
- Dropbox
- Bitbucket
- CloudFlare
- Canonical
- Heroku
- Zynga
- Disqus
- SendGrid
- and many many more...
Q&A
@akshay_deo
akshay@rainingclouds.com
RainingClouds is hiring Golang developers. Please mail us at : ping@rainingclouds.com