Go pattern with Golang Native using PostgreSQL, JWT & GORM.
- MVC Pattern (View not included)
- Pagination
- Role Management
- User Management
- Easy to manage your code
Run the server in project root
go run main.go
Change .env.example to .env
KEY | Value |
---|---|
DB_HOST | 127.0.0.1 |
DB_PORT | 5432 |
DB_USER | postgres |
DB_NAME | gopattern |
DB_PASSWORD | yourdbpassword |
SECRET | secretJWT |
Password | Role | |
---|---|---|
highadmin@gmail.com | password | High Admin |
normaladmin@gmail.com | password | Normal Admin |
List of endpoints for this starter
URL | Method | Description |
---|---|---|
/api/register | POST | Register a new user |
/api/login | POST | Logging a user |
/api/forgot-password | POST | Forgot password user |
/api/change-password/{token} | PATCH | Change / Reset password user |
Only High admin can access this & need token to access this
URL | Method | Description |
---|---|---|
/api/v1/roles | GET | Get all roles |
/api/v1/roles | POST | Creating a new role |
/api/v1/roles/{id} | GET | Get one role |
/api/v1/roles/{id} | PATCH | Update role |
/api/v1/roles/{id} | DELETE | Delete role |
/api/v1/users | GET | Get All Users |
Protected routes & need token to access this
URL | Method | Description |
---|---|---|
/api/v1/users/me | GET | Get profile / get authenticated user |
/api/v1/users/me/upload-image | POST | Upload image of authenticated user |
/api//users/me/delete-image | GET | Delete image of authenticated user |
I'm gonna recommend you,to make the controller inside app/controllers, it will make your controller more easy to import to use it in routing.
Everything that gonna interact with the database, you will place your model inside app/models.
For the middleware, it's located in app/middlewares. The default is contains for setting the type of response header and verifying the JWT.
You can configure your database setting in config/database.go.
The routing's located in routes folder.
All the core pagination functions located in app/helpers/pagination.go
Here's the main code:
package helpers
import (
"net/http"
"strconv"
)
/**
@desc PaginationResponse make pagination response way better
@param r *http.Request
@param limit int query string from param for limiting data
*/
func Pagination(r *http.Request, limit int) (int, int) {
keys := r.URL.Query()
if keys.Get("page") == "" {
return 1, 0
}
page, _ := strconv.Atoi(keys.Get("page"))
if page < 1 {
return 1, 0
}
begin := (limit * page) - limit
return page, begin
}
/**
@desc PaginationResponse make pagination response way better
@param *http.Request
@param page int query string from param for getting data from different page
@param pages int list of total pages (total / limit)
@param limit int query string from param for limiting data
@param total int total data in database
@param data interface{} presenting the data there's gonna be output
*/
func PaginationResponse(r *http.Request, page, pages, limit, total int, data interface{}) interface{} {
return map[string]interface{}{
"Links": map[string]interface{}{
"First": r.URL.Host + r.URL.Path + "?page=" + strconv.Itoa(page),
"Last": r.URL.Host + r.URL.Path + "?page=" + strconv.Itoa(pages),
"Prev": r.URL.Host + r.URL.Path + "?page=" + strconv.Itoa(page-1),
"Next": r.URL.Host + r.URL.Path + "?page=" + strconv.Itoa(page+1),
},
"Meta": map[string]interface{}{
"Limit": limit,
"Total": total,
"TotalPage": pages,
"CurrentPage": page,
"NextPage": page + 1,
"PreviousPage": page - 1,
"LastPage": pages,
},
"Results": data,
}
}
The code is self-explanatory in there
Here's the example for implementing pagination in the controller. You just insert the code down here. The URL only accept limit and name query string
package controllers
// Paginate the roles
queryParams := r.URL.Query()
limit, _ := strconv.Atoi(queryParams.Get("limit"))
nameParam := queryParams.Get("name")
if limit < 1 {
limit = 10
}
page, begin := helpers.Pagination(r, limit)
// @info total variable's from counting the roles in the model
pages := total / limit
if (total % limit) != 0 {
pages++
}
// Return the paginate
roles, err := role.GetRoles(begin, limit, nameParam, config.DB)
if err != nil {
helpers.Error(w, http.StatusBadRequest, err.Error())
return
}
// Map the data for better response
mapRoles := helpers.PaginationResponse(r, page, pages, limit, total, roles)
helpers.Success(w, http.StatusOK, "Roles list", mapRoles)
I'm making a helper for generating better and consistent http response with JSON. The file's located in app/helpers/json.go
package helpers
/**
@desc Success response JSON
@param w http.ResponseWriter
@param statusCode int
@param message string
@param data interface{}
*/
func Success(w http.ResponseWriter, statusCode int, message string, data interface{}) {
w.WriteHeader(statusCode)
response := map[string]interface{}{
"Status": "Success",
"Message": message,
"Data": data,
}
err := json.NewEncoder(w).Encode(response)
if err != nil {
fmt.Fprintf(w, "%s", err.Error())
}
}
/**
@desc Error response JSON
@param w http.ResponseWriter
@param statusCode int
@param message string
*/
func Error(w http.ResponseWriter, statusCode int, message string) {
w.WriteHeader(statusCode)
if message == "" {
message = "Something went wrong"
}
response := map[string]interface{}{
"Status": "Error",
"Message": message,
}
err := json.NewEncoder(w).Encode(response)
if err != nil {
fmt.Fprintf(w, "%s", err.Error())
}
}
You can simply use that http helpers JSON by write the example code below
package somewhere
import (
"gopattern/app/helpers"
"net/http"
)
func Test(w http.ResponseWriter, r *http.Request) {
// This is for returning a success response
someData := map[string]interface{}{"Name": "John Doe"}
helpers.Success(w, http.StatusOK, "Success!", someData)
return
// This is for returning a error response
explicitError := true
if explicitError == true {
helpers.Error(w, http.StatusInternalServerError, "Something went wrong!")
// YOU MUST RETURN IT! Otherwise it's gonna be running to the next code / statement
return
}
}
Author Huda Prasetyo 2020, All Right Reserved.