git clone https://github.com/go-tutorials/go-firestore-sample
cd go-firestore-sample
go run main.go
- GET: retrieve a representation of the resource
- POST: create a new resource
- PUT: update the resource
- PATCH: perform a partial update of a resource
- DELETE: delete a resource
To check if the service is available.
{
"status": "UP",
"details": {
"firestore": {
"status": "UP"
}
}
}
[
{
"id": "spiderman",
"username": "peter.parker",
"email": "peter.parker@gmail.com",
"phone": "0987654321",
"dateOfBirth": "1962-08-25T16:59:59.999Z"
},
{
"id": "wolverine",
"username": "james.howlett",
"email": "james.howlett@gmail.com",
"phone": "0987654321",
"dateOfBirth": "1974-11-16T16:59:59.999Z"
}
]
GET /users/wolverine
{
"id": "wolverine",
"username": "james.howlett",
"email": "james.howlett@gmail.com",
"phone": "0987654321",
"dateOfBirth": "1974-11-16T16:59:59.999Z"
}
{
"id": "wolverine",
"username": "james.howlett",
"email": "james.howlett@gmail.com",
"phone": "0987654321",
"dateOfBirth": "1974-11-16T16:59:59.999Z"
}
1
PUT /users/wolverine
{
"username": "james.howlett",
"email": "james.howlett@gmail.com",
"phone": "0987654321",
"dateOfBirth": "1974-11-16T16:59:59.999Z"
}
1
Perform a partial update of user. For example, if you want to update 2 fields: email and phone, you can send the request body of below.
PATCH /users/wolverine
{
"email": "james.howlett@gmail.com",
"phone": "0987654321"
}
1
If we pass a struct as a parameter, we cannot control what fields we need to update. So, we must pass a map as a parameter.
type UserService interface {
Update(ctx context.Context, user *User) (int64, error)
Patch(ctx context.Context, user map[string]interface{}) (int64, error)
}
We must solve 2 problems:
- At http handler layer, we must convert the user struct to map, with json format, and make sure the nested data types are passed correctly.
- At service layer or repository layer, from json format, we must convert the json format to database format (in this case, we must convert to bson of Mongo)
- At http handler layer, we use core-go/core, to convert the user struct to map, to make sure we just update the fields we need to update
import server "github.com/core-go/core"
func (h *UserHandler) Patch(w http.ResponseWriter, r *http.Request) {
var user User
userType := reflect.TypeOf(user)
_, jsonMap := sv.BuildMapField(userType)
body, _ := sv.BuildMapAndStruct(r, &user)
json, er1 := sv.BodyToJson(r, user, body, ids, jsonMap, nil)
result, er2 := h.service.Patch(r.Context(), json)
if er2 != nil {
http.Error(w, er2.Error(), http.StatusInternalServerError)
return
}
respond(w, result)
}
DELETE /users/wolverine
1
- core-go/health: include Health Handler, Firestore Health Checker
- core-go/config: to load the config file, and merge with other environments (SIT, UAT, ENV)
- core-go/log: log and log middleware
To check if the service is available, refer to core-go/health
{
"status": "UP",
"details": {
"firestore": {
"status": "UP"
}
}
}
To create health checker, and health handler
opts := option.WithCredentialsJSON([]byte(root.Credentials))
app, er1 := firebase.NewApp(ctx, nil, opts)
if er1 != nil {
return nil, er1
}
client, er2 := app.Firestore(ctx)
if er2 != nil {
return nil, er2
}
userService := services.NewUserService(client)
userHandler := handlers.NewUserHandler(userService)
firestoreChecker := firestore.NewHealthChecker(ctx, []byte(root.Credentials))
healthHandler := health.NewHealthHandler(firestoreChecker)
To handler routing
r := mux.NewRouter()
r.HandleFunc("/health", healthHandler.Check).Methods("GET")
To load the config from "config.yml", in "configs" folder
package app
import (
"github.com/core-go/log"
mid "github.com/core-go/log/middleware"
)
type Root struct {
Server ServerConfig `mapstructure:"server"`
Log log.Config `mapstructure:"log"`
MiddleWare mid.LogConfig `mapstructure:"middleware"`
Credentials string `mapstructure:"credentials"`
}
type ServerConfig struct {
Name string `mapstructure:"name"`
Port int64 `mapstructure:"port"`
}
import (
"github.com/core-go/config"
"github.com/core-go/log"
mid "github.com/core-go/log/middleware"
"github.com/gorilla/mux"
)
func main() {
var conf app.Root
config.Load(&conf, "configs/config")
r := mux.NewRouter()
log.Initialize(conf.Log)
r.Use(mid.BuildContext)
logger := mid.NewStructuredLogger()
r.Use(mid.Logger(conf.MiddleWare, log.InfoFields, logger))
r.Use(mid.Recover(log.ErrorMsg))
}
To configure to ignore the health check, use "skips":
middleware:
skips: /health