A Golang Beego boilerplate that can be deployed to the cloud without any setup or configuration using Hasura's free hosting tier. Make changes and "git push" to automatically roll out changes. Comes with Glide, condegangsta/gin, Golang Beego Dockerfile and Kubernetes spec files.
Table of Contents
- What's included?
- Deploying
- Viewing server logs and debugging
- Deploying your existing Beego app
- Managing dependencies
- Local development
- Adding backend features
- Beego web app boilerplate code with sample endpoints
- Glide for package management
- codegangsta/gin for live reloading in local development
- Dockerfile integrating the above
FROM golang:1.8.5-jessie # install required debian packages # add any package that is required after `build-essential`, end the line with \ RUN apt-get update && apt-get install -y \ build-essential \ && rm -rf /var/lib/apt/lists/* # install glide and gin RUN go get github.com/Masterminds/glide RUN go get github.com/codegangsta/gin # setup the working directory WORKDIR /go/src/app ADD glide.yaml glide.yaml ADD glide.lock glide.lock RUN mkdir /scripts ADD run-local-server.sh /scripts/run-local-server.sh RUN chmod +x /scripts/run-local-server.sh # install dependencies RUN glide install --skip-test # add source code ADD src src # build the source RUN go build src/main.go # command to be executed on running the container CMD ["./main"]
# quickstart from this boilerplate
$ hasura quickstart hello-golang-beego
# git push to deploy
$ cd hello-golang-beego
$ git add . && git commit -m "First commit"
$ git push hasura master
The quickstart command does the following:
- Creates a new directory hello-golang-beego in the current working directory
- Creates a free Hasura cluster and sets it as the default for this project
- Sets up hello-golang-beego as a git repository and adds hasura remote to push code
- Adds your SSH public key to the cluster so that you can push to it
Once deployed, access the app using the following command:
# open the beego app url in a browser
$ hasura microservice open app
.
├── Dockerfile # instructions to build the image
├── k8s.yaml # defines how the app is deployed
├── glide.yaml # lists dependent packages (handled by glide)
├── glide.lock # locked versions of packages (handled by glide)
├── run-local-server.sh # helper script to run local server
└── src
└── main.go # go source code
Edit the code in src/main.go
, for example, un-comment the commented lines:
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
)
// func sayPongJSON(ctx *context.Context) {
// ctx.Output.JSON(map[string]string{"message": "pong"}, true, true)
// }
func main() {
beego.Get("/", sayHello)
// beego.Get("/ping", sayPongJSON)
beego.Get("/get_articles", getArticles)
beego.Get("/profile", getProfile)
beego.Run() // listen and serve on 0.0.0.0:8080 by default
// set environment variable PORT if you want to change port
}
Commit and push to deploy again:
$ git add src/main.go
$ git commit -m "added new endpoint /ping"
$ git push hasura master
Checkout the new endpoint:
# open the url in browser
$ hasura microservice open app
# add /ping at the end of url
If the push fails with an error Updating deployment failed
, or the URL is showing 502 Bad Gateway
/504 Gateway Timeout
, follow the instruction on the page and checkout the logs to see what is going wrong with the microservice:
# see status of microservice app
$ hasura microservice list
# get logs for app
$ hasura microservice logs app
Fix the errors and deploy again.
- Replace contents of
src/
directory with your own source files - Leave
k8s.yaml
andDockerfile
as it is (they are used by Hasura to deploy the app) - Add your Go dependencies using
glide
(see adding go dependencies) - Add any system dependencies by editing
Dockerfile
(see adding system dependencies) - If you do not want to use glide, remove both yaml and lock files and edit
Dockerfile
to remove any references to them - If
src/
doesn't have amain.go
, edit last few lines ifDockerfile
to build and run the correct Go file
If you need to install a package, say github.com/gin-contrib/authz
# with docker
$ cd microservices/app
$ docker build -t hello-golang-beego-app .
$ docker run --rm -it -v $(pwd):/go/src/app \
hello-golang-beego-app \
glide get github.com/gin-contrib/authz
# without docker
$ cd microservices/app
$ glide get github.com/gin-contrib/authz
This will update glide.yaml
and glide.lock
.
The base image used in this boilerplate is golang:1.8.5-jessie. Hence, all debian packages are available for installation. You can add a package by mentioning it in the Dockerfile
among the existing apt-get install packages.
FROM golang:1.8.5-jessie
# install required debian packages
# add any package that is required after `build-essential`, end the line with \
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
...
- Install Docker CE
# go to app directory
$ cd microservices/app
# build the docker image
$ docker build -t hello-golang-beego-app .
# run the image using either 1 or 2
# 1) without live reloading
$ docker run --rm -it -p 8080:8080 hello-golang-beego-app
# 2) with live reloading
# any change you make to your source code will be immediately updated on the running app
# set CLUSTER_NAME
$ docker run --rm -it -p 8080:8080 \
-v $(pwd):/go/src/app \
-e CLUSTER_NAME=[your-cluster-name]
hello-golang-beego-app \
/scripts/run-local-server.sh
# app will be available at http://localhost:8080
# press Ctrl+C to stop the server
- Install Golang
- Move the
hello-golang-beego
directory to yourGOPATH
and cd into the directory
# change to app directory
$ cd mircoservices/app
# install glide for package management
$ go get github.com/Masterminds/glide
# install gin for live reloading
$ go get github.com/codegangsta/gin
# set CLUSTER_NAME
$ export CLUSTER_NAME=[your-cluster-name]
# run the local server script
# windows users: run this in git bash
$ ./run-local-server.sh
# app will be available at http://localhost:8080
# any change you make to your source code will be immediately updated on the running app
# press Ctrl+C to stop the server
Hasura comes with a few handy tools and APIs to make it easy to add backend features to your app.
When your app needs authentication, Hasura Auth APIs can be used to manage users, login, signup, logout etc. You can think of it like an identity service which takes away all the user management headaches from your app so that you can focus on your app's core functionality.
Just like the Data APIs, you can checkout and experiment with the Auth APIs using Hasura API Console.
Combined with database permission and API gateway session resolution, you can control which user or what roles have access to each row and column in any table.
For every request coming through external URL into a cluster, the API gateway tries to resolve a user based on a Cookie
or an Authorization
header. If none of them are present or are invalid, the following header is set and then the request is passed on to the upstream service:
X-Hasura-Role: anonymous
But, if the cookie or the authorization header does resolve to a user, gateway gets the user's ID and role from auth microservice and add them as headers before passing to upstream:
X-Hasura-User-Id: 3
X-Hasura-Role: user
Hence, other microservices need not manage sessions and can just rely on X-Hasura-Role
and X-Hasura-User-Id
headers.
admin
, user
, anonymous
are three built-in roles in the Hasura platform.
Hasura Auth APIs ship with a beautiful built-in user interface for common actions like login, signup etc. You can use these directly with your application and avoid building any user interface for logging in etc.
Edit conf/routes.yaml
and add the following lines to make the microservice accessible only to logged in users:
authorizationPolicy:
restrictToRoles: ["user"]
noSessionRedirectUrl: https://auth.{{ cluster.name }}.hasura-app.io/ui/
noAccessRedirectUrl: https://auth.{{ cluster.name }}.hasura-app.io/ui/restricted
Commit and push the new configuration:
$ git add conf/routes.yaml
$ git commit -m "restrict app to logged in users"
$ git push hasura master
Now, anyone who is not logged in will see a login prompt while visiting the microservice url.
# open the microservice url in a browser
$ hasura ms open app
When you don't want to restrict access to entire app, but only to particular path/APIs, you can check for the header X-Hasura-Role
and make a redirect if it is not what you require. For e.g. if you want to restrict access to /profile
only to logged in users, the route can look like the following:
func getProfile(ctx *context.Context) {
baseDomain := ctx.Input.Header("X-Hasura-Base-Domain")
if len(baseDomain) == 0 {
ctx.Abort(http.StatusInternalServerError, "This URL works only on Hasura clusters")
return
}
role := ctx.Input.Header("X-Hasura-Role")
if role == "user" {
userId := ctx.Input.Header("X-Hasura-User-Id")
ctx.Output.JSON(map[string]string{"userId": userId}, true, true)
} else {
redirectUrl := fmt.Sprintf(
"http://auth.%s/ui?redirect_to=http://app.%s/profile",
baseDomain, baseDomain,
)
ctx.Redirect(http.StatusTemporaryRedirect, redirectUrl)
}
}
When someone visits app.[cluster-name].hasura-app.io/profile
and is not logged in, they'll be redirected to login screen and after logging in or signing up, they're redirected back to /profile
.
Read more about Auth APIs here.
- Create required tables/columns using API Console (Data -> Schema)
- Use Query Builder under API Explorer and create the query
- Replicate the same JSON query in your beego app
To get all entries from article
table, the query would look like the following, using levigross/grequests:
resp, err := grequests.Post("https://data.hasura/v1/query",
&grequests.RequestOptions{
JSON: map[string]interface{}{
"type": "select",
"args": map[string]interface{}{
"table": "article",
"columns": []string{"*"},
},
},
},
)
if err != nil {
fmt.Printf("error: %s", err)
}
if !resp.Ok {
fmt.Printf("status code: %d, data: %s", resp.StatusCode, string(resp.Bytes())),
}
fmt.Printf("response: %s", string(resp.Bytes()))
The output will be something similar to:
[
{
"author_id": 12,
"content": "Vestibulum accumsan neque et nunc. Quisque...",
"id": 1,
"rating": 4,
"title": "sem ut dolor dapibus gravida."
},
{
"author_id": 10,
"content": "lacus pede sagittis augue, eu tempor erat neque...",
"id": 2,
"rating": 4,
"title": "nonummy. Fusce fermentum fermentum arcu."
},
...
]
Parameters required to connect to PostgreSQL on Hasura are already available as the following environment variables:
POSTGRES_HOSTNAME
POSTGRES_PORT
POSTGRES_USERNAME
POSTGRES_PASSWORD
You can use Go ORMs like go-pg/pq and jmoiron/sqlx to connect to Postgres. The database that Hasura uses for data and auth is called hasuradb
.
You can also checkout Beego ORM Usage.
Hasura APIs like data, auth etc. can be contacted using two URLs, internal and external. When your app is running inside the cluster, it can directly contact the Data APIs without any authentication. On the other hand, external URLs always go through the API Gateway, and hence special permissions will have to be applied over table for a non-authenticated user to access data.
http://data.hasura
- internal urlhttp://data.[cluster-name].hasura-app.io
- external url
PS: Hasura Data APIs are really powerful with nifty features like relationships, role based row and column level permissions etc. Using the APIs to their full potential will prevent you from re-inventing the wheel while building your app and can save a lot of time.
TIP: Use hasura ms list
to get all internal and external URLs available in your current cluster.
Read more about Data APIs here.
The best place to get started with APIs is Hasura API Console.
# execute inside the project directory
$ hasura api-console
This command will run a small web server and opens up API Console in a new browser tab. There are already some tables created along with this boilerplate, like article
and author
. These tables were created using migrations. Every change you make on the console is saved as a migration inside migrations/
directory.