Generating an rest api with go-gen-api

tl;dr

I generated an api with go-gen-api to speed up my development process.

Introduction

I often work with databases and rest services. Mostly I need to connect both services, doing some tasks on the database via the rest api, e.g. creating, updating or deleting records.
I had like 7 Tables and for each Table I needed to create a create, update, delete CRUD operation (Users, Groups, AccessRoles, Tokens, Resources, …). I don’t know how you create these CRUD operations but I was sick of writing the same code always again:

func Create(w http.ResponseWriter, r *http.Request)  {
    if r.Method != "POST" || r.Method != "PUT" {
    	throw some error
	}
    if err := json.unmarshal(r.Body, &newUserStruct); err != nil {
		throw some error
    }
    if err := validateUserStruct(newUserStruct); err != nil {
        throw some error
    }
    if err := createNewUserOnTable(&newUserStruct); err != nil {
        throw some error
    }
    send everything worked response
}

Writing a generator

So the objective is clear: Write an generator that auto generates all those crud operations

Thoughts

Coding

The main challenge was the structuring and templating, during coding I noticed that tables have null values, thats why the generated structs always use pointers.
Also how should the update work? The generator does not know what the primary key is (and I dont want him to know, remember? It should be simple). The solution is to split the request in a find and update field, meaning that the application will use the find field to find the records it needs to update and using the update field to set the values.
Another feature that is required for mostly all requirements is the Hooks feature. It enables you to modify the requested field before it gets passed to the database and at the same time it allows you to set a custom response for the query.

Finalizing

After working a few hours on the generator and I decided it is good enough for my needs, I stitched an example. (You can find it here). I realized that the code is still, well, a lot. At least I saved the whole Marshaling/Unmarshaling and Database operations, so it makes life a bit easier.

So what can we do now?

  1. We have an API that can be used for Database manipulation
  2. We have an REST-API that has an create, delete, update and get function for each specified table.

Sample of generation

type User struct {
	ID       int
	Name     string
	Password string
}

type Group struct {
	ID       int
	Name     string
}

type GroupMember struct {
	ID       int
	UserID   int
	GroupID  int
}

err := gogenapi.Generate(&gogenapi.Config{
	Structs:      []interface{}{&User{}, &Group{}, &GroupMember{}},
	OutputPath:   "gogenapi",
})
if err != nil {
	panic(err)
}

Sample of usage

package main

import (
	"database/sql"
	"log"
	"gogenapi/user" // Path to generated user
	"github.com/gorilla/mux"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "user.sqlite")
	router := mux.NewRouter()
	restAPI := user.NewRestAPI(router.PathPrefix("/user").Subrouter(), user.New(db))
	err = http.ListenAndServe(":8000", router)
	if err != nil {
		log.Fatal(err)
	}
}

Result

You can find the project here and the example here.

Written by

Tobias Salzmann