Directory Structure:
luamyproject/
|-- cmd/
| |-- myproject/
| |-- main.go
|-- internal/
| |-- handlers/
| | |-- handlers.go
| |-- models/
| |-- models.go
|-- pkg/
| |-- middleware/
| |-- logging.go
|-- go.mod
|-- go.sum
Initializing the Project:
shgo mod init myproject
Create your models in internal/models/models.go
.
gopackage models
type Item struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float64 `json:"price"`
}
Implement the handlers in internal/handlers/handlers.go
.
gopackage handlers
import (
"encoding/json"
"net/http"
"myproject/internal/models"
"strconv"
"github.com/gorilla/mux"
)
var items []models.Item
func GetItems(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(items)
}
func GetItem(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
http.Error(w, "Invalid item ID", http.StatusBadRequest)
return
}
for _, item := range items {
if item.ID == id {
json.NewEncoder(w).Encode(item)
return
}
}
http.Error(w, "Item not found", http.StatusNotFound)
}
func CreateItem(w http.ResponseWriter, r *http.Request) {
var item models.Item
_ = json.NewDecoder(r.Body).Decode(&item)
item.ID = len(items) + 1
items = append(items, item)
json.NewEncoder(w).Encode(item)
}
func UpdateItem(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
http.Error(w, "Invalid item ID", http.StatusBadRequest)
return
}
for index, item := range items {
if item.ID == id {
items = append(items[:index], items[index+1:]...)
var updatedItem models.Item
_ = json.NewDecoder(r.Body).Decode(&updatedItem)
updatedItem.ID = id
items = append(items, updatedItem)
json.NewEncoder(w).Encode(updatedItem)
return
}
}
http.Error(w, "Item not found", http.StatusNotFound)
}
func DeleteItem(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
http.Error(w, "Invalid item ID", http.StatusBadRequest)
return
}
for index, item := range items {
if item.ID == id {
items = append(items[:index], items[index+1:]...)
break
}
}
json.NewEncoder(w).Encode(items)
}
Set up the router in cmd/myproject/main.go
.
gopackage main
import (
"log"
"myproject/internal/handlers"
"net/http"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/items", handlers.GetItems).Methods("GET")
router.HandleFunc("/items/{id}", handlers.GetItem).Methods("GET")
router.HandleFunc("/items", handlers.CreateItem).Methods("POST")
router.HandleFunc("/items/{id}", handlers.UpdateItem).Methods("PUT")
router.HandleFunc("/items/{id}", handlers.DeleteItem).Methods("DELETE")
log.Fatal(http.ListenAndServe(":8000", router))
}
Add logging middleware in pkg/middleware/logging.go
.
gopackage middleware
import (
"log"
"net/http"
"time"
)
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.RequestURI, time.Since(start))
})
}
Apply the middleware in main.go
.
gorouter.Use(middleware.LoggingMiddleware)
Use tools like Postman or cURL to test your endpoints.
GET all items:
shcurl -X GET http://localhost:8000/items
GET a single item:
shcurl -X GET http://localhost:8000/items/1
Create a new item:
shcurl -X POST http://localhost:8000/items -d '{"name":"Item1","description":"A new item","price":10.99}' -H "Content-Type: application/json"
Update an item:
shcurl -X PUT http://localhost:8000/items/1 -d '{"name":"Updated Item","description":"Updated description","price":12.99}' -H "Content-Type: application/json"
Delete an item:
shcurl -X DELETE http://localhost:8000/items/1
Enhance error handling in your handlers.
gofunc CreateItem(w http.ResponseWriter, r *http.Request) {
var item models.Item
if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
http.Error(w, "Invalid input", http.StatusBadRequest)
return
}
item.ID = len(items) + 1
items = append(items, item)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(item)
}
Implement authentication and authorization mechanisms such as JWT or OAuth2.
Example with JWT:
gofunc AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" || !validateToken(token) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
Apply the middleware to routes that require authentication.
gorouter.HandleFunc("/items", handlers.GetItems).Methods("GET")
router.Handle("/items", AuthMiddleware(http.HandlerFunc(handlers.CreateItem))).Methods("POST")
Use tools like Swagger to document your API.
Swagger Integration:
Install go-swagger
:
shgo get -u github.com/go-swagger/go-swagger/cmd/swagger
Add comments to your handlers:
go// swagger:route GET /items items listItems
//
// Lists all items.
//
// Responses:
// 200: itemsResponse
func GetItems(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(items)
}
Generate the documentation:
shswagger generate spec -o ./swagger.yaml --scan-models
Serve the Swagger UI with your API.
By following these steps, you'll have a robust and maintainable RESTful API built in Go, capable of handling real-world requirements and scalable for future growth.