I am trying to put database transaction in context so I can commit or rollback right before sending a response. I have trouble passing the context between packages. Even thought I set the value for given key in service package, in server package for the same key, values is nil. Does anyone know how to solve this issue ?
package server
import (
"e-commerce/models"
"encoding/json"
"net/http"
)
func (s *Server) CreateCategory(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
var category models.Category
// decoding json message to user model
err := json.NewDecoder(req.Body).Decode(&category)
if err != nil {
returnResponse(ctx, w, http.StatusBadRequest, err)
return
}
_, err = s.service.CreateCategory(ctx, &category)
if err != nil {
returnResponse(ctx, w, http.StatusBadRequest, err)
return
}
returnResponse(ctx, w, http.StatusOK, err)
return
}
package services
import (
"context"
"e-commerce/models"
)
func (s Service) CreateCategory(ctx context.Context, category *models.Category) (*models.Category, error) {
err := category.Validate()
if err != nil {
return nil, err
}
tx := s.userRepository.Db().Begin()
context.WithValue(ctx, "transaction", tx)
result := s.userRepository.Db().Create(&category)
if result.Error != nil {
return nil, result.Error
}
return category, nil
}
package server
import (
"context"
"e-commerce/config"
"e-commerce/services"
"github.com/gorilla/mux"
"gorm.io/gorm"
"net/http"
)
func returnResponse(ctx context.Context, w http.ResponseWriter, status int, err error) {
tx := ctx.Value("transaction")
if status != 200 {
tx.(*gorm.DB).Rollback()
} else {
tx.(*gorm.DB).Commit()
}
w.WriteHeader(status)
if err != nil {
w.Write([]byte(err.Error()))
}
}
Adding values to a context works like the layers of an onion: Each new added value creates a new context wrapping the old one. The new context has the added value, but the old one doesn't. If you cancel a context, all contexts wrapping it will be canceled.
So, if you want to call a function that adds things to a context, you can:
Alternatively, you can prepare the context in the handler, so other functions can modify it: