Inspired by go-gin-boilerplate for:
- Swagger
- Gin
- Modular approach in go (no gorm dependency of controllers)
Have a template for starting a REST API with ORM integration and hot reload with go air.
Setup 3 environments: dev, stage and prod
- cd docker/dev
- docker compose up
docker exec -it dev-db-1 bash
su - postgres
psql -h localhost -p 5432 -U <user> <db_name>
psql -h localhost -p 5432 -U postgres postgres
\l
: list DBs\c <db_name>
: connect to DB\dt
: list all the tablesselect \* from <table_name>;
select \* from users;
cd <project_root>
SERVER_ENVIRONMENT="dev" go run main.go
Running:
go run main.go
is the same as:
go build -o out && ./out
Uses Gin-Gonic
Uses GORM
cd ~/go/src/github.com/pavva91/gin-gorm-rest/
gofmt -l -s -w .
Go Air enables hot reloading in go. Usage of air:
- Create of config file (.air.toml):
air init
- Run app with hot reload (inside project root):
air
Instead of:go run main.go
go run main.go server_config.go
Tutorial: https://codewithmukesh.com/blog/jwt-authentication-in-golang/ Check JWT token decoder Dependencies:
- go get github.com/dgrijalva/jwt-go (deprecated)
- go get -u github.com/golang-jwt/jwt/v5 (up to date)
- go get golang.org/x/crypto/bcrypt
- Authenticate (create JWT token for the user account):
curl -X POST http://127.0.0.1:8080/api/v1/token -d '{"email":"[email protected]", "password":"1234"}'
Response:{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlODkiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsImV4cCI6MTY4MDI2MDU2OH0.6d9-WiCQTAcs4wxIkqHyQ3J0-UZBEr2_swpdUcO7zRc" }
- Authorized Call:
curl -X GET http://127.0.0.1:8080/api/v1/secured/ping -H 'Accept: application/json' -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlODkiLCJlbWFpbCI6ImFsaWNlQGdtYWlsLmNvbSIsImV4cCI6MTY4MDI2MDU2OH0.6d9-WiCQTAcs4wxIkqHyQ3J0-UZBEr2_swpdUcO7zRc'
Uses Clean Env:
- go get -u github.com/ilyakaznacheev/cleanenv
swag fmt && swag init
Uses Swag
go install github.com/swaggo/swag/cmd/swag@latest
Note: Go install tries to install the package into $GOBIN, when $GOBIN=/usr/local/go/bin will not work, works with $GOBIN=~/go/bin Initialize Swag (on project root):swag init
Then get dependencies:go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Format Swag Comments:
swag fmt
Swagger API:
http://localhost:8080/swagger/index.html#/
Inspired by:
Zero allocation JSON logger, zerolog:
go get -u github.com/rs/zerolog/log
Gin uses [Go Validator v10] (https://github.com/go-playground/validator):
go get github.com/go-playground/validator/v10
In code import:
import "github.com/go-playground/validator/v10"
The linter will help to write idiomatic go. On project root run:
golangci-lint run
The linter configuration is: .golangci.yml
linters:
enable-all: true
go get -u github.com/stretchr/testify
- Run all tests:
go test ./...
- Run all tests and create code coverage report:
go test -v -coverprofile cover.out ./...
- Run all tests with code coverage and open on browser:
go test -v -coverprofile cover.out ./... && go tool cover -html=cover.out
- Run tests of /controllers with code coverage and open on browser:
go test -v -coverprofile cover.out ./controllers/ && go tool cover -html=cover.out
- Run tests of /services with code coverage and open on browser:
go test -v -coverprofile cover.out ./services/ && go tool cover -html=cover.out
- Run tests of /repositories with code coverage and open on browser:
go test -v -coverprofile cover.out ./repositories/ && go tool cover -html=cover.out
- Run specific tests (regex):
go test -run TestMyFunction ./...
- Run specific tests (regex):
go test -run Test_GenerateToken_InvalidRequestBodyNoPasswordField_400BadRequest ./...
- Run specific tests (regex) of a module (e.g. controllers):
go test -run Test_GenerateToken ./controllers
- Run tests of a folder (e.g. validation):
go test ./validation
- Run tests of a module (e.g. validation):
go test github.com/pavva91/gin-gorm-rest/validation
- Run particular test (e.g. test Test_GetByID_EmptyId_400BadRequest in /controllers/user_test.go):
go test -v ./... -run Test_GetByID_EmptyId_400BadRequest
- Run particular test (e.g. test Test_GetByID_EmptyId_400BadRequest in /controllers/user_test.go):
go test -v ./controllers -run Test_GetByID_EmptyId_400BadRequest
- Run particular test with code coverage report (e.g. test Test_GetByID_EmptyId_400BadRequest in /controllers/user_test.go):
go test -v -coverprofile cover.out ./controllers -run Test_GetByID_EmptyId_400BadRequest && go tool cover -html=cover.out
- Run particular test when using test suite (e.g. test Test_Status_Return200 in TestSuiteHealthController in /controllers/health_test.go):
go test -v ./controllers -run TestSuiteHealthController -testify.m Test_Status_Return200
-
By package name:
- Just run:
go test -cover github.com/pavva91/gin-gorm-rest/validation
- Create coverage file:
go test -v -coverprofile cover.out github.com/pavva91/gin-gorm-rest/validation
- One Command create coverage file and open in browser;
go test -v -coverprofile cover.out github.com/pavva91/gin-gorm-rest/controllers && go tool cover -html=cover.out
- Just run:
-
By folder:
- Just run:
go test -cover ./validation
- Create coverage file:
go test -v -coverprofile cover.out ./validation
- Open coverage file on browser:
go tool cover -html=cover.out
- One Command create coverage file and open in browser;
go test -v -coverprofile cover.out ./controllers/ && go tool cover -html=cover.out
- Just run:
-
Run all tests:
- Just run:
go test ./... -cover
- Create coverage file:
go test ./... -coverprofile coverage.out
- Open coverage file on browser:
go tool cover -html=coverage.out
- Just run:
From stack overflow
- Create function in ~/.bashrc and/or ~/.zshrc:
cover () {
t="/tmp/go-cover.$$.tmp"
go test -coverprofile=$t $@ && go tool cover -html=$t && unlink $t
}
- Call this function:
cd ~/go/src/github.com/pavva91/gin-gorm-rest/
cover github.com/pavva91/gin-gorm-rest/validation
- :lua require('dap-go').debug_test()
- Keymap: dt
- List users: GET /users:
curl -X GET http://127.0.0.1:8080/users
- Create user: POST /users:
curl -X POST http://127.0.0.1:8080/users -H 'Content-Type: application/json' -d '{"name":"mario", "email":"[email protected]", "password":"1234"}'
- Get a user: GET /users/1:
curl -X GET http://127.0.0.1:8080/users/1
- Modify a user: PUT /users/1:
curl -X PUT http://127.0.0.1:8080/users/1 -H 'Content-Type: application/json' -d '{"name":"john", "email":"[email protected]", "password":"5678"}'
- Delete a user: DELETE /users/1:
curl -X DELETE http://127.0.0.1:8080/users/1
DB Management inside neovim through dadbod (tpope/vim-dadbod, kristijanhusak/vim-dadbod-ui, kristijanhusak/vim-dadbod-completion):
- :DBUI (<leader>db is :DBUIToggle)
- Connection to db (Add connection):
- postgres://<user>:<password>@localhost:<port>/<db_name>
- In case of default values (dev db)
- postgres://postgres:localhost@localhost:5432/postgres
Queries:
SELECT * FROM users
SELECT name, username, email FROM users WHERE "name" = 'Bob'