I. Introduction
- I usually write articles to find out if the viblo already exists, if there is one I will not write it anymore, but in this series for you to have an overview, I will again write an article about authenticate jwt with golang.
- This article I will introduce you to the base that I built by myself is also very delicious
II. Deployment
First, I will introduce the folder structure for you to easily imagine in advance and I will present from a-> z respectively.
Looks like laravel, right?
- First, I will create a helper to customize the response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package helper import "strings" type Response struct { Status bool `json:"status"` Message string `json:"message"` Errors interface{} `json:"errors"` Data interface{} `json:"data"` } type EmptyObj struct{} func BuildResponse(status bool, message string, data interface{}) Response { res := Response{ Status: status, Message: message, Errors: nil, Data: data, } return res } func BuildErrorResponse(message string, err string, data interface{}) Response { splittedError := strings.Split(err, "n") res := Response{ Status: false, Message: message, Errors: splittedError, Data: data, } return res } |
- As everyone knows, I will run the command
go run index.go
to start the server so see what this file contains (I use the gin framework to implement this series)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import ( "golang_api/routes" "gorm.io/gorm" "golang_api/config" ) var ( db *gorm.DB = config.SetupDatabaseConnection() ) func main() { defer config.CloseDatabaseConnection(db) router := routes.InitRouter() router.Run() } |
As the code, you see this file will call the router and connect database,
- Setup database in config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package config import ( "fmt" "os" "github.com/joho/godotenv" "golang_api/app/model" "gorm.io/driver/mysql" "gorm.io/gorm" ) func SetupDatabaseConnection() *gorm.DB { errEnv := godotenv.Load() if errEnv != nil { panic("Failed to load env file") } dbUser := os.Getenv("DB_USER") dbPass := os.Getenv("DB_PASS") dbHost := os.Getenv("DB_HOST") dbName := os.Getenv("DB_NAME") dsn := fmt.Sprintf("%s:% <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> (%s:3306)/%s?charset=utf8&parseTime=True&loc=Local", dbUser, dbPass, dbHost, dbName) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("Failed to create a connection to database") } db.AutoMigrate(&model.Book{}, &model.User{}) return db } func CloseDatabaseConnection(db *gorm.DB) { dbSQL, err := db.DB() if err != nil { panic("Failed to close connection from database") } dbSQL.Close() } |
- In the
routes
folder, create anindex.go
file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package routes import ( "golang_api/app/http/controller" "golang_api/app/http/middleware" "golang_api/app/service" "golang_api/app/repository" "github.com/gin-gonic/gin" "golang_api/config" "gorm.io/gorm" ) var ( db *gorm.DB = config.SetupDatabaseConnection() userRepository repository.UserRepository = repository.NewUserRepository(db) jwtService service.JWTService = service.NewJWTService() authService service.AuthService = service.NewAuthService(userRepository) authController controller.AuthController = controller.NewAuthController(authService, jwtService) ) func InitRouter() *gin.Engine { routes := gin.Default() authRoutes := routes.Group("api/auth") { authRoutes.POST("/login", authController.Login) authRoutes.POST("/register", authController.Register) } return routes } |
Here I create 2 APIs that are login
, register
for authenticate authentication. continue to see what authController sẽ xử lý gì nhé
. As initialized, you can see that I have used the service
, repository
… so let’s see what these instances do in turn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package controller import ( "net/http" "strconv" "github.com/gin-gonic/gin" "golang_api/app/http/request" "golang_api/app/model" "golang_api/helper" "golang_api/app/service" ) type AuthController interface { Register(ctx *gin.Context) } type authController struct { authService service.AuthService jwtService service.JWTServic } func NewAuthController(authService service.AuthService, jwtService service.JWTService) AuthController { return &authController{ authService: authService, jwtService: jwtService, } } func (c *authController) Register(ctx *gin.Context) { var registerRequest request.RegisterRequest errRequest := ctx.ShouldBind(&registerRequest) if errRequest != nil { response := helper.BuildErrorResponse("Failed to process request", errRequest.Error(), helper.EmptyObj{}) ctx.AbortWithStatusJSON(http.StatusBadRequest, response) return } } |
As in the routes
I imported, in this controller I called the server
, the repository
to use. The first is to implement the Register
function, the first thing is to validate, and I have created a misty validate file.
1 2 3 4 5 6 7 | package request type RegisterRequest struct { Name string `json:"name" form:"name" binding:"required"` Email string `json:"email" form:"email" binding:"required,email" ` Password string `json:"password" form:"password" binding:"required"` } |
Next, after passing the validate, go to the processing step, here I check duplicate mail and then create an account.
1 2 3 4 5 6 7 8 9 10 11 12 | ... if !c.authService.IsDuplicateEmail(registerRequest.Email) { response := helper.BuildErrorResponse("Failed to process request", "Duplicate email", helper.EmptyObj{}) ctx.JSON(http.StatusConflict, response) } else { createdUser := c.authService.CreateUser(regist erRequest) token := c.jwtService.GenerateToken(strconv.FormatUint(createdUser.ID, 10)) createdUser.Token = token response := helper.BuildResponse(true, "OK!", createdUser) ctx.JSON(http.StatusCreated, response) } |
Function to check duplicate mail
1 2 3 4 5 6 | authService.go func (service *authService) IsDuplicateEmail(email string) bool { res := service.userRepository.IsDuplicateEmail(email) return !(res.Error == nil) } |
1 2 3 4 5 6 | userRepository.go func (db *userConnection) IsDuplicateEmail(email string) (tx *gorm.DB) { var user model.User return db.connection.Where("email = ?", email).Take(&user) } |
Now, let’s test it with the default port 8080. This is the case without pass validate
And this is the pass validate case
III. End
Since I write quite in detail both about the code and the project folder structure, it is quite long, so I will contribute login and crud for the next post.