Golang'da REST API qanday yaratiladi (batafsil qo'llanma)
Golang'da REST API yaratmoqchimisiz? API'ingizni sozlash, kodini yozish va deploy qilish uchun best practice'lar va tushunarli misollar bilan to'ldirilgan batafsil qo'llanmamizga amal qiling.
Muallif: William Imoh

Go'da REST API yaratish yuqori unumdorlik, deploy qilish qulayligi va scalability'ni o'zida birlashtiradi. Go'ning samarali runtime'i va built-in concurrency modeli uni katta hajmdagi so'rovlarni qayta ishlaydigan va shu bilan birga past latency'ni saqlab qoladigan ilovalar yaratish uchun ideal tanlovga aylantiradi. Mustahkam standart kutubxonasi va qo'llab-quvvatlovchi ekotizimi tufayli ilovalaringizga REST funksionalligini tezda qo'shishingiz mumkin.
2009-yilda rasmiy chiqarilganidan beri Go keng ommalashdi va kichik ko'lamdagi tizimlardan tortib mission-critical tizimlargacha bo'lgan ilovalarni yaratish uchun asosiy texnologiyaga aylanib bormoqda. Soha yetakchilari undan Docker va Kubernetes kabi containerization va orchestration vositalarini yaratishda foydalanganidan so'ng uning ommabopligi yanada keskin oshdi. Kichik servis yoki mission-critical ilova yaratayotganingizdan qat'i nazar, Go REST API ishlab chiqish uchun kuchli poydevor beradi.
Ushbu afzalliklarni hisobga olib, keling, Go imkoniyatlarini amalda qo'llab ko'ramiz. Bu qo'llanmada siz create, read, update va delete (CRUD) funksionalligini qo'llab-quvvatlaydigan, kitoblar hisobini yurituvchi oddiy boshqaruv API'sini yaratasiz. Shuningdek, bu funksiyalar kutilganidek ishlashiga ishonch hosil qilish uchun ularga unit test'lar yozasiz, API'larni himoyalash uchun authentication'ni joriy qilasiz va to'liq hujjatlar tayyorlaysiz.
Keling, ilovani yaratishni boshlaymiz.
1-qadam: Loyihani sozlash
Boshlash uchun terminal yordamida loyiha papkasini yarating.
mkdir go_book_api && cd go_book_api
Bu buyruqlar go_book_api nomli papka yaratadi va unga o'tadi.
Keyin loyiha ichida Go module'ni initialize qiling.
go mod init go_book_api
Bu buyruq loyiha dependency'larini kuzatib borish uchun go.mod faylini yaratadi.
Nihoyat, kerakli dependency'larni o'rnating.
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres
go get -u gorm.io/driver/sqlite
go get -u github.com/joho/godotenv
Keling, bu dependency'lar nima ekanini va ular nima uchun ishlatilishini ko'rib chiqamiz:
Gin — veb-ilovalar yaratish uchun freymvork.
Gorm — Go'ga asoslangan Object Relational Mapper (ORM). ORM'lar ma'lumotlar bazasidagi ma'lumotlarga query yuborish va ularni o'zgartirishni osonlashtiradi. O'rnatish paytida Postgres va SQLite driver'lari ham qo'shiladi. Ilova ma'lumotlari bilan ishlash uchun Postgres driver'idan foydalanasiz, SQLite esa unit test'lar yozishda ma'lumotlar bazasi bilan o'zaro ta'sirni mock qiladi.
godotenv — environment variable'larni yuklash uchun kutubxona.
Loyiha ma'lumotlar bazasini sozlash API ma'lumotlaringizni saqlash uchun loyihangiz ma'lumotlar bazasini Neon'da sozlashingiz kerak. Neon — boshlash uchun saxiy bepul tarifga ega, to'liq boshqariladigan serverless PostgreSQL ma'lumotlar bazasi.
Ma'lumotlar bazasini sozlash uchun Neon console'ga kiring va loyiha yarating.

Loyiha muvaffaqiyatli yaratilgach, ma'lumotlar bazasi URL'ini nusxalab oling.

Buning o'rniga istalgan managed Postgres servisi yoki lokal Postgres instance'idan ham foydalanishingiz mumkin.
Va nihoyat, loyihangiz papkasida .env faylini yarating va unga nusxalangan ma'lumotlar bazasi URL'ini qo'shing.
DB_URL=<REPLACE THIS WITH YOUR DATABASE URL>
2-qadam: Loyiha tuzilmasini yaratish
Go loyihangizni qanday tuzishingizni majburlamasa-da, Go bilan ilova yaratishda yaxshi tuzilmaga ega bo'lish juda muhim. Bu codebase'ingizni qo'llab-quvvatlashni osonlashtiradi va boshqalar uchun tushunarli qiladi.
Loyihangizni tuzish uchun api, cmd va tests papkalarini yarating.
api — API ishlab chiqishga oid fayllarni tartibga solish uchun.
cmd — ilovaning entry point'ini joylashtirish uchun ishlatiladi. Bu Go hamjamiyatida qabul qilingan konvensiya.
tests — unit test'larni tartibga solish uchun.
3-qadam: Kitoblar hisobini yurituvchi ilova uchun CRUD amallarini yozish
Ilova ma'lumotlari va javoblarining tuzilmasini belgilash uchun api/model.go faylini yarating.
package api
import "github.com/gin-gonic/gin"
type Book struct {
ID uint `json:"id" gorm:"primaryKey"`
Title string `json:"title"`
Author string `json:"author"`
Year int `json:"year"`
}
type JsonResponse struct {
Status int `json:"status"`
Message string `json:"message"`
Data any `json:"data"`
}
func ResponseJSON(c *gin.Context, status int, message string, data any) {
response := JsonResponse{
Status: status,
Message: message,
Data: data,
}
c.JSON(status, response)
}
Yuqoridagi kod parchasi struct tag'lar (masalan, json:"title") yordamida kerakli property'larga ega Book va JsonResponse struct'larini hamda API javoblarini boshqarish uchun ResponseJSON yordamchi funksiyasini yaratadi.
Struct tag JSON nomlash konvensiyasiga mos keladigan javoblarni yuborish imkonini beradi.
Yangi kitob yaratish
Yangi kitob yaratuvchi handler funksiyasini yozish uchun ilovaning business logic'ini o'z ichiga oladigan api/handlers.go faylini yarating. Business logic'ni data model'dan ajratish muhim, chunki bu scalable, qo'llab-quvvatlashga qulay va test qilish oson bo'lgan kod yozishga yordam beradi.
package api
import (
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
"net/http"
"os"
)
var DB *gorm.DB
func InitDB() {
err := godotenv.Load()
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
dsn := os.Getenv("DB_URL")
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// migrate the schema
if err := DB.AutoMigrate(&Book{}); err != nil {
log.Fatal("Failed to migrate schema:", err)
}
}
func CreateBook(c *gin.Context) {
var book Book
//bind the request body
if err := c.ShouldBindJSON(&book); err != nil {
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
return
}
DB.Create(&book)
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
}
Kod parchasi ma'lumotlar bazasi ulanishini saqlaydigan global DB o'zgaruvchisini e'lon qiladi, so'ngra u InitDB funksiyasi ichida initialize qilinadi. InitDB funksiyasi ma'lumotlar bazasiga ulanish uchun environment variable'ni yuklaydi va avtomatik schema migration'ni bajaradi. Nihoyat, yangi kitob yaratish, xatolarni qayta ishlash va ResponseJSON yordamchisi orqali tegishli javobni qaytarish uchun DB o'zgaruvchisidan foydalanadigan CreateBook handler'ini yarating.
Funksiya, type yoki o'zgaruvchi bosh harf bilan boshlanadigan nom bilan e'lon qilinsa, u export qilingan bo'ladi. Bu u boshqa package'larga ko'rinishini va ular undan foydalana olishini anglatadi.
Kitoblar ro'yxatini olish
Kitoblar ro'yxatini olish uchun ma'lumotlar bazasidagi mavjud kitoblar ro'yxatini DB o'zgaruvchisi orqali oladigan GetBooks handler'ini yarating.
func GetBooks(c *gin.Context) {
var books []Book
DB.Find(&books)
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
}
Bitta kitobni olish
Kitobni olish uchun DB o'zgaruvchisi orqali ko'rsatilgan ID'ga mos kitobni oladigan va tegishli javobni qaytaradigan GetBook handler'ini yarating.
func GetBook(c *gin.Context) {
var book Book
if err := DB.First(&book, c.Param("id")).Error; err != nil {
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
return
}
ResponseJSON(c, http.StatusOK, "Book retrieved successfully", book)
}
Kitobni yangilash
Kitobni yangilash uchun DB o'zgaruvchisi orqali ko'rsatilgan ID'ga mos kitobni oladigan, uni yangilaydigan va tegishli javobni qaytaradigan UpdateBook handler'ini yarating.
func UpdateBook(c *gin.Context) {
var book Book
if err := DB.First(&book, c.Param("id")).Error; err != nil {
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
return
}
// bind the request body
if err := c.ShouldBindJSON(&book); err != nil {
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
return
}
DB.Save(&book)
ResponseJSON(c, http.StatusOK, "Book updated successfully", book)
}
Kitobni o'chirish
Kitobni o'chirish uchun DB o'zgaruvchisi orqali ko'rsatilgan ID'ga mos kitobni oladigan, uni o'chiradigan va tegishli javobni qaytaradigan DeleteBook handler'ini yarating.
func DeleteBook(c *gin.Context) {
var book Book
if err := DB.Delete(&book, c.Param("id")).Error; err != nil {
ResponseJSON(c, http.StatusNotFound, "Book not found", nil)
return
}
ResponseJSON(c, http.StatusOK, "Book deleted successfully", nil)
}
Hammasini birlashtirish
Handler'lar tayyor bo'lgach, ilovaning entry point'ini yaratishingiz va ilova route'larini belgilashingiz kerak. Buning uchun cmd/main.go faylini yarating va unga quyidagi kod parchasini qo'shing:
package main
import (
"github.com/gin-gonic/gin"
"go_book_api/api"
)
func main() {
api.InitDB()
r := gin.Default()
//routes
r.POST("/book", api.CreateBook)
r.GET("/books", api.GetBooks)
r.GET("/book/:id", api.GetBook)
r.PUT("/book/:id", api.UpdateBook)
r.DELETE("/book/:id", api.DeleteBook)
r.Run(":8080")
}
Yuqoridagi kod parchasi ma'lumotlar bazasini initialize qiladigan, route'larni ularga tegishli handler'lar bilan belgilaydigan va ilovani 8080 port'ida ishga tushiradigan main funksiyasini yaratadi.
4-qadam: REST API'larni test qilish
API'larni test qilish uchun sizga Postman yoki o'zingiz tanlagan istalgan API test qilish ilovasi kerak bo'ladi.
Terminalingizda quyidagi buyruqni ishga tushirib, development server'ni ishga tushiring.
go run cmd/main.go
So'ngra, quyida ko'rsatilganidek, endpoint'larni sinab ko'rish uchun o'zingiz tanlagan API test qilish client'idan foydalaning:
Kitob yaratish endpoint'i
Yangi kitob yaratish uchun POST method'idan foydalaning va kerakli ma'lumotlarni uzating.
curl --location 'localhost:8080/book' \
--header 'Content-Type: application/json' \
--data '{
"title": "Go by Example: Programmer'\''s guide to idiomatic and testable code",
"author": "Inanc Gumus",
"year": 2021
}'

Kitoblar ro'yxatini olish
Ma'lumotlar bazasidagi mavjud kitoblar ro'yxatini olish uchun GET method'idan foydalaning.
curl --location 'localhost:8080/books'

Kitobni olish endpoint'i
Mavjud kitob tafsilotlarini olish uchun GET method'idan foydalaning va ID'ni qo'shing.
curl --location 'localhost:8080/book/3'

Kitobni yangilash endpoint'i
Mavjud kitobni yangilash uchun PUT method'idan foydalaning hamda ID va yangi ma'lumotlarni ko'rsating.
curl --location --request PUT 'localhost:8080/book/3' \
--header 'Content-Type: application/json' \
--data '{
"title": "Go by Example: Programmer'\''s guide to idiomatic and testable code - Updated",
"author": "Inanc Gumus",
"year": 2023
}'

Kitobni o'chirish
Avval yaratilgan kitob ID'si bilan DELETE so'rovini yuboring.
curl --location --request DELETE 'localhost:8080/book/3'

Eng so'nggi o'zgarishlarni ko'rish uchun Neon ma'lumotlar bazangizga query yuborishingiz ham mumkin.

5-qadam: API handler funksiyalarini unit test qilish
Go o'zining testing package'i bilan birga keladi va u unit test'lar, integration test'lar, functional test'lar hamda end-to-end test'lar yozishni osonlashtiradi. Boshlash uchun tests/main_test.go faylini yarating va unga quyidagi kod parchasini qo'shing:
package tests
import (
"bytes"
"encoding/json"
"github.com/gin-gonic/gin"
"go_book_api/api"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"net/http"
"net/http/httptest"
"strconv"
"testing"
)
func setupTestDB() {
var err error
api.DB, err = gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
panic("failed to connect test database")
}
api.DB.AutoMigrate(&api.Book{})
}
func addBook() api.Book {
book := api.Book{Title: "Go Programming", Author: "John Doe", Year: 2023}
api.DB.Create(&book)
return book
}
Yuqoridagi kod parchasi setupTestDB va addBook yordamchi funksiyalarini yaratadi. Ular mos ravishda faqat test maqsadlari uchun yangi SQLite in-memory ma'lumotlar bazasini (:memory: ) initialize qiladi va test ma'lumotlar bazasiga yangi kitob yozuvini qo'shadi.
Test fayli nomini
_test.gobilan tugatish Go ekotizimidagi standart nomlash konvensiyasidir.
Yangi kitob yaratish uchun unit test
Gin yordamida mock HTTP server'ni sozlaydigan, kitob yaratish uchun POST so'rovini yuboradigan hamda javob kutilgan natijaga va CreateBook handler'ida belgilangan HTTP status kodiga mos kelishini tekshiradigan TestCreateBook test funksiyasini yarating.
func TestCreateBook(t *testing.T) {
setupTestDB()
router := gin.Default()
router.POST("/book", api.CreateBook)
book := api.Book{
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
}
jsonValue, _ := json.Marshal(book)
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusCreated {
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Data == nil {
t.Errorf("Expected book data, got nil")
}
}
Kitoblar ro'yxatini olish uchun unit test
TestCreateBook test funksiyasiga o'xshab, GET so'rovi orqali kitoblar ro'yxatini olish bo'yicha GetBooks handler funksionalligini test qiladigan TestGetBooks funksiyasini yarating.
func TestGetBooks(t *testing.T) {
setupTestDB()
addBook()
router := gin.Default()
router.GET("/books", api.GetBooks)
req, _ := http.NewRequest("GET", "/books", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if len(response.Data.([]interface{})) == 0 {
t.Errorf("Expected non-empty books list")
}
}
Bitta kitobni olish uchun unit test
GET so'rovi orqali ko'rsatilgan ID'ga mos bitta kitobni olish bo'yicha GetBook handler funksionalligini test qiladigan TestGetBook test funksiyasini yarating.
func TestGetBook(t *testing.T) {
setupTestDB()
book := addBook()
router := gin.Default()
router.GET("/book/:id", api.GetBook)
req, _ := http.NewRequest("GET", "/book/"+strconv.Itoa(int(book.ID)), nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Data == nil || response.Data.(map[string]interface{})["id"] != float64(book.ID) {
t.Errorf("Expected book ID %d, got nil or wrong ID", book.ID)
}
}
Kitobni yangilash uchun unit test
PUT so'rovi orqali mavjud kitobni yangilash bo'yicha UpdateBook handler funksionalligini test qiladigan TestUpdateBook test funksiyasini yarating.
func TestUpdateBook(t *testing.T) {
setupTestDB()
book := addBook()
router := gin.Default()
router.PUT("/book/:id", api.UpdateBook)
updateBook := api.Book{
Title: "Advanced Go Programming", Author: "Demo Author name", Year: 2021,
}
jsonValue, _ := json.Marshal(updateBook)
req, _ := http.NewRequest("PUT", "/book/"+strconv.Itoa(int(book.ID)), bytes.NewBuffer(jsonValue))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Data == nil || response.Data.(map[string]interface{})["title"] != "Advanced Go Programming" {
t.Errorf("Expected updated book title 'Advanced Go Programming', got %v", response.Data)
}
}
Kitobni o'chirish uchun unit test
DELETE so'rovi orqali mavjud kitobni o'chirish bo'yicha DeleteBook handler funksionalligini test qiladigan TestDeleteBook test funksiyasini yarating.
func TestDeleteBook(t *testing.T) {
setupTestDB()
book := addBook()
router := gin.Default()
router.DELETE("/book/:id", api.DeleteBook)
req, _ := http.NewRequest("DELETE", "/book/"+strconv.Itoa(int(book.ID)), nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Message != "Book deleted successfully" {
t.Errorf("Expected delete message 'Book deleted successfully', got %v", response.Message)
}
//verify that the book was deleted
var deletedBook api.Book
result := api.DB.First(&deletedBook, book.ID)
if result.Error == nil {
t.Errorf("Expected book to be deleted, but it still exists")
}
}
Nihoyat, quyidagi buyruq yordamida test'ingizni development muhitida ishga tushirishingiz mumkin:
GIN_MODE=release go test tests/main_test.go -v
GIN_MODE=release— Gin'dagi buyruq bo'lib, u Gin'ga production muhitini taqlid qilishni va test'ni optimallashtirilgan rejimda ishga tushirishni bildiradi.

REST API'larga authentication qo'shish
Mustahkam API yaratishning ajralmas qismi — authentication mexanizmi. U API'ingizdan foydalanayotgan foydalanuvchi yoki tizimning shaxsini tasdiqlaydi. API'ingizda authentication sozlangan bo'lsa, siz:
- Maxfiy resurslar yoki ma'lumotlarga ruxsatsiz kirishning oldini olasiz.
- Kirish darajasini nazorat qilasiz.
- API'ga kim kirgani va qanday amallarni bajarganini kuzatib, log'ga yozib borasiz.
- Authenticated foydalanuvchilarga moslashtirilgan javoblar yoki tajriba taqdim etasiz.
Go'da authentication'ni quyidagi usullardan biri bilan amalga oshirishingiz mumkin:
- JSON Web Tokens (JWT).
- OAuth 2.0.
- Session-based authentication.
- API key'lar.
Bu loyihada API'ni himoyalash uchun JWT (JSON Web Tokens)'dan foydalanasiz. Jarayon foydalanuvchi o'z credential'larini, masalan, username va parolini taqdim etishidan boshlanadi. Keyin tizim bu credential'larni tekshiradi va token beradi, foydalanuvchi esa undan himoyalangan endpoint'larga kirish uchun foydalanishi mumkin.
Boshlash uchun JWT package'ini o'rnating.
go get -u github.com/golang-jwt/jwt/v5
Middleware yaratish
Middleware'lar — HTTP so'rovlarini yakuniy route handler'ga yetib borishidan oldin yoki javob handler'dan chiqqanidan keyin ushlab qoladigan va qayta ishlaydigan funksiyalar yoki modullar. Bu holatda siz istalgan endpoint'ga murojaat qilinishidan oldin so'rovda to'g'ri token borligini tekshirmoqchisiz.
Buning uchun avval .env faylingizga secret token qo'shing. Bu token token'larning xavfsizligi, haqiqiyligi va yaxlitligini ta'minlaydi.
SECRET_TOKEN=<REPLACE WITH A RANDOM LONG STRING>
Keyin api papkasi ichida middleware.go faylini yarating va unga quyidagi kod parchasini qo'shing:
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"net/http"
"os"
)
// Secret key for signing JWT
var jwtSecret = []byte(os.Getenv("SECRET_TOKEN"))
func JWTAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
ResponseJSON(c, http.StatusUnauthorized, "Authorization token required", nil)
c.Abort()
return
}
// parse and validate the token
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Validate the signing method
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return jwtSecret, nil
})
if err != nil {
ResponseJSON(c, http.StatusUnauthorized, "Invalid token", nil)
c.Abort()
return
}
// Token is valid, proceed to the next handler
c.Next()
}
}
Yuqoridagi kod parchasi JWT package'idan foydalanib kiruvchi so'rovlarni ushlab qoladigan va so'rovni handler'ga o'tkazishdan oldin ularning JWT token'larini validate qiladigan JWTAuthMiddleware funksiyasini yaratadi.
Keyin handlers.go fayliga standart username va parol asosida JWT token yaratadigan GenerateJWT funksiyasini qo'shing.
package api
import (
//other imports
"github.com/golang-jwt/jwt/v5"
)
func GenerateJWT(c *gin.Context) {
var loginRequest LoginRequest
if err := c.ShouldBindJSON(&loginRequest); err != nil {
ResponseJSON(c, http.StatusBadRequest, "Invalid request payload", nil)
return
}
if loginRequest.Username != "admin" || loginRequest.Password != "password" {
ResponseJSON(c, http.StatusUnauthorized, "Invalid credentials", nil)
return
}
expirationTime := time.Now().Add(15 * time.Minute)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"exp": expirationTime.Unix(),
})
// Sign the token
tokenString, err := token.SignedString(jwtSecret)
if err != nil {
ResponseJSON(c, http.StatusInternalServerError, "Could not generate token", nil)
return
}
ResponseJSON(c, http.StatusOK, "Token generated successfully", gin.H{"token": tokenString})
}
Real ilovada token yaratish uchun ishlatiladigan credential'lar standart emas, balki har bir foydalanuvchiga xos bo'ladi.
Va nihoyat, token yaratish uchun public route qo'shish va API route'larini middleware yordamida himoyalash uchun cmd papkasi ichidagi main.go faylini yangilang.
package main
import (
"github.com/gin-gonic/gin"
"go_book_api/api"
)
func main() {
api.InitDB()
r := gin.Default()
// Public routes
r.POST("/token", api.GenerateJWT)
// protected routes
protected := r.Group("/", api.JWTAuthMiddleware())
{
protected.POST("/book", api.CreateBook)
protected.GET("/books", api.GetBooks)
protected.GET("/book/:id", api.GetBook)
protected.PUT("/book/:id", api.UpdateBook)
protected.DELETE("/book/:id", api.DeleteBook)
}
r.Run(":8080")
}
REST API'ni test qilish
Server'ni qayta ishga tushirib, token'siz istalgan endpoint'ga so'rov yuborsangiz, unauthorized javobini olasiz.
curl --location 'localhost:8080/books'

API'ni token bilan test qilish uchun token yaratish endpoint'iga so'rov yuboring.
curl --location 'localhost:8080/token' \
--header 'Content-Type: application/json' \
--data '{
"username": "admin",
"password": "password"
}'

So'ngra yaratilgan token yordamida so'rov yuboring.
curl --location 'localhost:8080/books' \
--header 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM3MDU3Njl9.ULspm6GR9Q0zqZWHifdFEeLZqgtw7k2FDDhSOpwcw4U'

Unit test'ni yangilash
API route'i endi himoyalangani sababli, authentication mexanizmini hisobga olish uchun unit test'ni ham yangilashingiz kerak.
package tests
import (
// other imports
"github.com/golang-jwt/jwt/v5"
)
var jwtSecret = []byte(os.Getenv("SECRET_TOKEN"))
func setupTestDB() {
// setupTestDB goes here
}
func addBook() api.Book {
// add book code goes here
}
func generateValidToken() string {
expirationTime := time.Now().Add(15 * time.Minute)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"exp": expirationTime.Unix(),
})
tokenString, _ := token.SignedString(jwtSecret)
return tokenString
}
func TestGenerateJWT(t *testing.T) {
router := gin.Default()
router.POST("/token", api.GenerateJWT)
loginRequest := map[string]string{
"username": "admin",
"password": "password",
}
jsonValue, _ := json.Marshal(loginRequest)
req, _ := http.NewRequest("POST", "/token", bytes.NewBuffer(jsonValue))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
t.Errorf("Expected status %d, got %d", http.StatusOK, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Data == nil || response.Data.(map[string]interface{})["token"] == "" {
t.Errorf("Expected token in response, got nil or empty")
}
}
Yuqoridagi kod parchasi kerakli dependency'ni import qiladi hamda generateValidToken yordamchisini va TestGenerateJWT funksiyasini yaratadi: ular mos ravishda token yaratadi va token yaratish endpoint'ini test qiladi.
Keyin generateValidToken yordamchi funksiyasidan foydalanib, har bir test uchun request obyektini o'zgartiring: route'ga authentication qo'shing, token yarating va uni so'rov header'iga qo'shing. Masalan, kitob yaratish test'i quyidagicha ko'rinadi:
func TestCreateBook(t *testing.T) {
setupTestDB()
router := gin.Default()
protected := router.Group("/", api.JWTAuthMiddleware()) // add
protected.POST("/book", api.CreateBook) // add
token := generateValidToken() // add
book := api.Book{
Title: "Demo Book name", Author: "Demo Author name", Year: 2021,
}
jsonValue, _ := json.Marshal(book)
req, _ := http.NewRequest("POST", "/book", bytes.NewBuffer(jsonValue))
req.Header.Set("Authorization", token) // add
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if status := w.Code; status != http.StatusCreated {
t.Errorf("Expected status %d, got %d", http.StatusCreated, status)
}
var response api.JsonResponse
json.NewDecoder(w.Body).Decode(&response)
if response.Data == nil {
t.Errorf("Expected book data, got nil")
}
}
REST API'larga hujjatlar qo'shish
Hujjatlar API'laringizning ajralmas qismidir. Ular maqsadingizni yetkazishga hamda boshqalarga API'laringizni tushunish, qo'llab-quvvatlash va samarali ishlatishga yordam beradi. Hujjatlar quyidagi ko'rinishlarda bo'lishi mumkin:
- Texnik hujjatlar — dasturchilarga API'lar, konfiguratsiyalar va tizim arxitekturasi bo'yicha yo'l-yo'riq beradi.
- Foydalanuvchi qo'llanmasi — oxirgi foydalanuvchilarga mahsulotdan qanday foydalanishni tushunishga yordam beradi.
- Ichki hujjatlar — ichki jarayonlar, workflow'lar va boshqa operatsion tafsilotlarni qayd etadi.
- Kod izohlari — kodning muayyan qismlariga bevosita kod ichida tushuntirish beradi.
Go'da hujjatlarni yaratish, qo'llab-quvvatlash va ko'rsatish uchun turli vositalardan foydalanishingiz mumkin. Quyida ulardan eng ko'p ishlatiladiganlari keltirilgan:
- GoDoc
- pkg.go.dev
- go doc
- godocdown
- Swagger/OpenAPI
Bu loyihada hujjatlar qo'shish uchun GoDoc'dan foydalanasiz.
Boshlash uchun avval operatsion tizimingizga mos ravishda Go versiyasini yangilang. Keyin quyidagi buyruq yordamida GoDoc package'ini o'rnating:
go get golang.org/x/tools/cmd/godoc
Keyin handlers.rs faylini o'zgartirib, har bir yordamchi funksiya va handler nima qilishini tavsiflovchi izohlar qo'shing.
/*
Package api provides RESTful handlers and middleware for managing a library of books.
It includes functionality for creating, reading, updating, and deleting books, as well as generating JWT tokens for authentication.
*/
package api
import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
"net/http"
"os"
"time"
)
var DB *gorm.DB
// InitDB initializes the database connection using environment variables.
// It loads the database configuration from a .env file and migrates the Book schema.
func InitDB() {
err := godotenv.Load()
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
dsn := os.Getenv("DB_URL")
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// Migrate the schema
if err := DB.AutoMigrate(&Book{}); err != nil {
log.Fatal("Failed to migrate schema:", err)
}
}
// CreateBook handles the creation of a new book in the database.
// It expects a JSON payload with book details and responds with the created book.
func CreateBook(c *gin.Context) {
var book Book
if err := c.ShouldBindJSON(&book); err != nil {
ResponseJSON(c, http.StatusBadRequest, "Invalid input", nil)
return
}
DB.Create(&book)
ResponseJSON(c, http.StatusCreated, "Book created successfully", book)
}
// GetBooks retrieves all books from the database.
// It responds with a list of books.
func GetBooks(c *gin.Context) {
var books []Book
DB.Find(&books)
ResponseJSON(c, http.StatusOK, "Books retrieved successfully", books)
}
// other handlers goes below
Nihoyat, quyidagi buyruqni ishga tushirib hujjatlarni tekshirib ko'ring:
godoc -http=:6060
Brauzeringizni oching va hujjatlar URL'iga o'ting:
http://localhost:6060/pkg/go_book_api/api/

Keyingi qadamlar
Go'ning soddaligi, built-in concurrency'si, cloud-native imkoniyatlari va unumdorligi uni dasturchilar orasida mashhur tanlovga aylantiradi. O'sib borayotgan ekotizim va hamjamiyat yordami esa RESTful API'larni, shuningdek siz yaratmoqchi bo'lgan boshqa har qanday ilovani samarali yaratish va qo'llab-quvvatlash uchun kerakli vositalar va kutubxonalarga ega bo'lishingizni ta'minlaydi.
Gin freymvorkidan tashqari Go'da API yaratish uchun Echo, Revel va boshqa ko'plab mashhur veb-freymvorklar mavjud. Eng so'nggi o'zgarishlardan xabardor bo'lib turish uchun Go roadmap'ga qarang.