آموزش کار با RESTful API در گولنگ با Gin, Gorm و Redis

در این مقاله به آموزش ساخت یک RESTful API در زبان برنامه‌نویسی Go (گولنگ) با استفاده از فریم‌ورک قدرتمند Gin، کتابخانه ORM محبوب Gorm و سیستم کش Redis می‌پردازیم. هدف این آموزش، ساخت یک API سریع، مقیاس‌پذیر و بهینه است که بتواند به‌صورت مؤثر با پایگاه‌داده ارتباط برقرار کند و هم‌زمان با استفاده از Redis عملکرد بهتری ارائه دهد. با دنبال کردن این آموزش، با نحوه پیاده‌سازی ساختار پایه API، اتصال به دیتابیس، انجام عملیات CRUD و استفاده از کش Redis آشنا خواهید شد.


مرور کلی

  • Gin: یک فریم‌ ورک وب HTTP با عملکرد بالا در زبان گو است که برای ساخت APIهای RESTful بسیار مناسب است.
  • Gorm: یک ORM برای زبان گولنگ است که رابطی سطح بالا برای کار با پایگاه‌داده فراهم می‌کند.
  • Redis: به‌عنوان لایه کش مورد استفاده قرار می‌گیرد تا با ذخیره‌سازی داده‌های پرکاربرد در حافظه، بار روی پایگاه‌داده را کاهش دهد.


عملیات هایی که انجام میدیم امروز

  • راه‌اندازی یک ساختار پایه برای API با استفاده از Gin

  • اتصال به پایگاه‌داده و انجام عملیات CRUD با Gorm

  • افزودن کش Redis برای بهبود عملکرد API


پیش‌نیازها

  • نصب بودن زبان گولنگ (Golang)

  • نصب بودن ردیس (Redis)

  • نصب دیتابیسPostgreSQL یا MySQL


ساختار پروژه

codenews/
├── main.go
├── config/
│   ├── config.go
│   └── database.go
├── controllers/
│   └── user_controller.go
├── models/
│   └── user.go
├── repositories/
│   └── user_repository.go
├── services/
│   └── user_service.go
├── cache/
│   └── redis.go
└── routes/
    └── routes.go


1. راه اندازی کانفیگ

یک فایل در مسیر config/config.go ایجاد کنید. این فایل قراره کانفیگ اپلیکیشن ما باشه و تمامی کانفیگ ها در این فایل قرار میگیره:

package config

import (
    "fmt"
    "os"

    "github.com/joho/godotenv"
)

type Config struct {
    DBHost     string
    DBPort     string
    DBUser     string
    DBPassword string
    DBName     string
    RedisAddr  string
    RedisPassword string
    RedisDB int
}

var AppConfig *Config

func LoadConfig() {
    if err := godotenv.Load(); err != nil {
       fmt.Println("No .env file found")
    }

    AppConfig = &Config{
       DBHost:     os.Getenv("DB_HOST"),
       DBPort:     os.Getenv("DB_PORT"),
       DBUser:     os.Getenv("DB_USER"),
       DBPassword: os.Getenv("DB_PASSWORD"),
       DBName:     os.Getenv("DB_NAME"),
       RedisAddr:  os.Getenv("REDIS_ADDR"),
       RedisPassword: os.Getenv("REDIS_PASSWORD"),
       RedisDB: 0,
    }
}

در اینجا ما کانفیگ های دیتابیس و ردیس ست کردیم. شما در فایل .env خودتون این مقادیر رو میتونید پر کنید.

در اینجا ما از پکیج godotenv استفاده کردیم، شما میتونید با استفاده از go mod tidy بیاید این پکیج ها نصب کنید یا دستی خودتون مستقیم نصب کنید و این موارد من در این پست دیگه اشاره نمیکنم، چون اصولا خودتون بلد هستید🤟


2. اتصال به دیتابیس با استفاده از Gorm

برای اتصال به دیتابیس ما یک فایل در مسیر config/database.go ایجاد میکنیم. در اینجا عملیات وصل شدن به دیتابیس با استفاده از Gorm انجام میدیم:

package config

import (
    "fmt"
    "log"

    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

var DB *gorm.DB

func ConnectDatabase() {
    var err error
    dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable",
       AppConfig.DBHost, AppConfig.DBUser, AppConfig.DBPassword, AppConfig.DBName, AppConfig.DBPort)

    DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
    if err != nil {
       log.Fatal("Failed to connect to database!", err)
    }

    log.Println("Database connected")
}

در اینجا با استفاده از فایل کانفیگ که در قسمت 1 ایجاد کردیم، اومدیم از کانفیگ دیتابیس استفاده کردیم و همینطور یک متغیر Global ایجاد کردیم به نام DB که به دیتابیس دسترسی داشته باشیم در کل اپلیکیشن.


3. راه اندازی کش با استفاده از ردیس (Redis)

برای راه اندازی کش در اپلیکیشن یک فایل در مسیر cache/redis.go ایجاد میکنیم تا بتونیم ردیس رو راه اندازی کنیم:

package cache

import (
    "context"
    "github.com/redis/go-redis/v9"
    "myapp/config"
    "time"
)

var RedisClient *redis.Client

func InitRedis() {
    RedisClient = redis.NewClient(&redis.Options{
       Addr:     config.AppConfig.RedisAddr,
       Password: config.AppConfig.RedisPassword,
       DB:       config.AppConfig.RedisDB,
    })
}

func SetCache(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
    return RedisClient.Set(ctx, key, value, expiration).Err()
}

func GetCache(ctx context.Context, key string) (string, error) {
    return RedisClient.Get(ctx, key).Result()
}

در اینجا ما علاوه بر اینکه کانفیگ ردیس راه اندازی کردیم، دو متد به نام "SetCache()" و "GetCache()" درست کردیم که بتونیم در ردیس کلید ذخیره یا دریافت کنیم.


4. ساخت مدل

برای ساخت مدل ها یک فایل در مسیر models/user.go ایجاد میکنیم تا مدل ها بتونیم در این پوشه ایجاد کنیم و نظم بهتری داشته باشه:

package models

type User struct {
    ID    uint   'json:"id" gorm:"primaryKey"'
    Name  string 'json:"name"'
    Email string 'json:"email" gorm:"unique"'
}

در اینجا مدل User ایجاد کردیم با 3 پراپرتی، که شما میتونید پراپرتی های دیگه هم تعریف کنید.


5. لایه ریپازیتوری (Repository)

ما یک لایه ریپازیتوری برای گرفتن دیتا از دیتابیس ایجاد میکنیم. برای ساخت ریپازیتوری از این مسیر repositories/user_repository.go پیروی کنید:

package repositories

import (
    "codenews/cache"
    "codenews/config"
    "codenews/models"
    "context"
    "fmt"
    "time"
)

func GetUserByID(ctx context.Context, id uint) (*models.User, error) {
    cacheKey := fmt.Sprintf("user:%d", id)

    // Try to get user from cache
    cachedUser, err := cache.GetCache(ctx, cacheKey)
    if err == nil {
       fmt.Println("Cache hit")
       return cachedUser, nil
    }
    fmt.Println("Cache miss")

    // If not in cache, fetch from DB
    var user models.User
    if err := config.DB.First(&user, id).Error; err != nil {
       return nil, err
    }

    // Store in cache for 10 minutes
    cache.SetCache(ctx, cacheKey, user, 10*time.Minute)
    return &user, nil
}

در اینجا ما یک متد ساختیم که با استفاده از ID بیایم یک کاربر از دیتابیس دریافت کنیم، اما در کنار این کار از کش برای کش کردن دیتا کاربر استفاده کردیم که هر 10 دقیقه این کش منقضی میشه.


6. لایه سرویس (Service)

برای ارتباط بین کنترلر و ریپازیتوری ما یک لایه سرویس هم اضافه میکنیم. این کار اختیاریه و شما میتونید لایه سرویس حذف یا حتی برای عملیات های ذخیره استفاده کنید. برای ساخت سرویس از مسیر services/user_service.go پیروی کنید:

package services

import (
    "context"
    "codenews/models"
    "codenews/repositories"
)

func GetUser(ctx context.Context, id uint) (*models.User, error) {
    return repositories.GetUserByID(ctx, id)
}

در اینجا ما مستقیم از ریپازیتوری دیتا دریافت کردیم.


7. کنترلر (Controller)

برای ساخت کنترلر در مسیر controllers/user_controller.go یک کنترلر ایجاد میکنیم. کنترلر ها وظیفه گرفتن دیتا و نمایش آن به صورت JSON و.. دارند:

package controllers

import (
    "net/http"
    "strconv"
    "github.com/gin-gonic/gin"
    "codenews/services"
)

func GetUser(c *gin.Context) {
    id, _ := strconv.Atoi(c.Param("id"))
    user, err := services.GetUser(c, uint(id))
    if err != nil {
       c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
       return
    }

    c.JSON(http.StatusOK, user)
}

در اینجا ما از سرویس دیتا کاربر دریافت کردیم و با استفاده از فریم ورک Gin در قالب JSON نمایش دادیم.


8. روتنیگ (Routing)

یک فایل در مسیر routes/routes.go برای سیستم روت اپلیکیشن ایجاد میکنیم. با استفاده از فریم ورک جین میایم روتینگ هندل میکنیم:

package routes

import (
    "github.com/gin-gonic/gin"
    "codenews/controllers"
)

func RegisterRoutes(router *gin.Engine) {
    router.GET("/users/:id", controllers.GetUser)
}


9. بخش اصلی اپلیکیشن (Main)

خب هر اپلیکیشن گولنگی به main نیازمند هست پس ایجادش میکنیم در روت پروژه:

package main

import (
    "github.com/gin-gonic/gin"
    "codenews/config"
    "codenews/cache"
    "codenews/routes"
)

func main() {
    // Load configurations
    config.LoadConfig()

    // Connect to database
    config.ConnectDatabase()

    // Initialize Redis
    cache.InitRedis()

    // Set up router
    router := gin.Default()
    routes.RegisterRoutes(router)

    // Start the server
    router.Run(":8080")
}

در main ما میایم هر بخش راه اندازی میکنیم. این بخش هایی که میگیم یعنی دیتابیس، کش (ردیس)، کانفیگ و.. تا اپلیکیشن ما به درستی کار کنه.

نکته: در اینجا ما مستقیما پورت ران شدن برنامه مشخص کردیم که شما میتونید پورت هم از کانفیگ بخونید.



0 🔥
0 🎉
0 😮
0 👍
0 💜
0 👏
میلاد خسروی
نویسنده کد نیوز

برنامه نویس فان | Fun Developer یک آدم ساده که عاشق برنامه نویسی و کد زدنه :) تلاش میکنه تا به بقیه کمک کنه. توسعه دهنده هسته لاراول و فضای اوپن سورس. فاندر پرانتز و کد نیوز.

0+ نظر

برای ثبت نظر ابتدا ورود کنید.

0 نظر

    اولین نفر باش که نظر ثبت میکنی :) یعنی یه کامنت به ما نمیرسه 😁