Golang ile Basit Bir Web API’si Nasıl Oluşturulur: Fiber ve GORM ile CRUD Uygulaması

Kadir Doğuş Seçkin
4 min readApr 12, 2024

--

Bu makalede Fiber ve GORM kullanarak Go ve MySQL üzerinde inşaa edilmiş bir CRUD uygulaması geliştireceğiz. Fiber ve GORM, Go dilinde uygulamalar geliştirmek için kullanılan iki popüler kütüphanedir.

GORM, Go için geliştirilmiş, ORM özellikleri sunan bir kütüphanedir. Bu kütüphane sayesinde structlar aracılığı ile veritabanı işlemlerimizi kolayca gerçekleştirebiliriz.

Fiber, API işlemlerimizi kolayca gerçekleştirmemizi sağlayan ve routing,middleware gibi sıklıkla ihtiyaç duyulan yapıları içinde barındıran bir web frameworkudur.

Projemizdeki klasör yapısı aşağıdaki gibi olacaktır.

İlk önce projemize GORM ve Fiber paketlerini dahil edelim. Ayrıca MySQL üzerinde çalışacağımız için GORM MySQL driveri ve .env dosyamızdaki configleri yönetebilmek için godotenv paketlerini de dahil ediyoruz.

 go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
go get -u github.com/gofiber/fiber/v2
go get github.com/joho/godotenv

.env dosyasına aşağıdaki gibi veritabanı bilgilerini ekleyebiliriz.

DB_USER_NAME = db_user_name
DB_PASSWORD = db_password
DB_HOST = db_name
DB_NAME = dd_name

Buradaki bilgileri okuyabilmek için config klasörü altındaki config.go dosyasında bir fonksiyon oluşturuyoruz. Bu fonksiyon godotenv paketi yardımıyla ayarları okumamızı sağlayacak.

func Get(key string) string {
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Config error %s", err)
}

return os.Getenv(key)
}

Daha sonrasında bu fonksiyona veritabanı bağlantısını açarken ihtiyacımız olacak. database.go içinde GORM ve MySQL kullanarak veritabanı bağlantısını yapan fonksiyonu tanımlayalım.

var DB *gorm.DB

func StartConnection() {
var dbUserName = config.Get("DB_USER_NAME")
var dbPassword = config.Get("DB_PASSWORD")
var dbHost = config.Get("DB_HOST")
var dbName = config.Get("DB_NAME")
var dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s", dbUserName, dbPassword, dbHost, dbName)

db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
log.Fatalf("Database connection error %s", err)
}

DB = db

// Create products table
DB.AutoMigrate(&model.Product{})
}

Bu fonksiyonu çağırdıktan sonra DB üzerinden veri tabanı işlemlerini yapabileceğiz. Ayrıca AutoMigrate fonksiyonunu kullanarak veritabanı üzerinde yeni bir products tablosu oluşturduk. Bu GORM’un bize sağladığı kolaylıklardan biridir. Structlar üzerinden veritabanı işlemleri yapmamıza olanak sağlar. Burada kullanılan model klasörü altındaki Product struct’ı aşağıdaki gibidir.

type Product struct {
Id int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Price float64 `db:"price" json:"price"`
}

Router klasörü altındaki router.go içinde api endpointlerini tanımlayacağız.

func SetRoutes(app *fiber.App) {
// Add "/api" prefix for all routes
apiRouter := app.Group("/api")

// Products routes
apiRouter.Get("/products", api.GetAllProducts)
apiRouter.Get("/products/:id", api.GetProductById)
apiRouter.Post("/products", api.CreateProduct)
apiRouter.Put("/products/:id", api.UpdateProduct)
apiRouter.Delete("/products/:id", api.DeleteProduct)
}

Tüm CRUD operasyonları için birer end point oluşturduk. Ayrıca bunların başına “/api” prefixini ekledik.

Tanımladığımız fonksiyonlar *fiber.Ctx parametresi almalı ve error dönecek şekilde tanımlanmalıdır. handler.go içinde tanımlanan CRUD fonksiyonları aşağıdaki gibidir.

func GetAllProducts(ctx *fiber.Ctx) error {
var products []model.Product
database.DB.Find(&products)

if len(products) == 0 {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"data": nil,
"error": "Products not found",
})
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"data": products,
"error": nil,
})
}


func GetProductById(ctx *fiber.Ctx) error {
var product model.Product
var id = ctx.Params("id")
database.DB.First(&product, id)

if product.Id == 0 {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"data": nil,
"error": "Product not found",
})
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"data": product,
"error": nil,
})
}


func CreateProduct(ctx *fiber.Ctx) error {
var product model.Product
var err = ctx.BodyParser(&product)

if err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"data": nil,
"error": "Invalid request",
})
}

err = database.DB.Create(&product).Error
if err != nil {
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"data": nil,
"error": "Create operation failed",
})
}

return ctx.Status(fiber.StatusCreated).JSON(fiber.Map{
"data": product,
"error": nil,
})
}


func UpdateProduct(ctx *fiber.Ctx) error {
var updatedProduct model.Product
var err = ctx.BodyParser(&updatedProduct)

if err != nil {
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"data": nil,
"error": "Invalid request",
})
}

var id = ctx.Params("id")
var oldProduct model.Product
database.DB.First(&oldProduct, id)

if oldProduct.Id == 0 {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"data": nil,
"error": "Product not found",
})
}

// Prevent id update
err = database.DB.Model(&oldProduct).Select("name", "price").Updates(model.Product{
Name: updatedProduct.Name,
Price: updatedProduct.Price,
}).Error

if err != nil {
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"data": nil,
"error": "Update operation failed",
})
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"data": updatedProduct,
"error": nil,
})
}


func DeleteProduct(ctx *fiber.Ctx) error {
var product model.Product
var id = ctx.Params("id")
database.DB.First(&product, id)

if product.Id == 0 {
return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
"data": nil,
"error": "Product not found",
})
}

var err = database.DB.Delete(&product, id).Error
if err != nil {
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"data": nil,
"error": "Delete operation failed",
})
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"data": product,
"error": nil,
})
}

Tüm bu tanımlamalar yapıldığında main.go içinde aşağıdaki gibi ufak bir kod parçası yazarak projemizi çalışır hale getirebiliriz.

func main() {
app := fiber.New()
database.StartConnection()
router.SetRoutes(app)
err := app.Listen(":8080")
if err != nil {
log.Fatalf("Error listen server %s", err)
}
}

Veritabanına bir kaç kayıt ekledikten sonra http://127.0.0.1:8080/api/products adresine istek attığımızda aşağıdaki gibi bir sonuç alırız.

Okuduğunuz için teşekkür ederim.

--

--