Chào các bạn, trong bài viết này mình xin giới thiệu với các bạn cách tạo 1 ứng dụng REST API đơn giản sử dụng Fiber
và Postgresql
. Mục tiêu của bài viết để giới thiệu cho những người mới làm quen với Go và Fiber về cách hoạt động cũng như sự đơn giản của ngôn ngữ và framework này.
Bắt đầu thôi nào 😁😁
Tạo project
Bước đầu tiên chúng ta sẽ khởi tạo thư mục chứa project
$ mkdir crud-app
$ cd crud-app
Trong thư mục chứa project, chúng ta tạo go module
cho project
$ go mod init fiber-postgres
Cấu trúc của project của chúng ta như sau
.
├── controller // Code xử lý routes
├── ddl // Các câu lệnh ddl tạo bảng
├── go.mod
├── go.sum
├── main.go // File chạy chính
├── model // Định nghĩa các model có trong project
├── repo // Code xử lý thao tác trực tiếp với CSDL
├── router // Định nghĩa các routes
└── test // Viết các unit test
Tạo cơ sở dữ liệu postgresql
Trong bài viết này chúng ta sẽ sử dụng GoPG để kết nối với hệ CSDL Postgresql, vì vậy chúng ta cần cài đặt Postgresql trước
Chúng ta có thể cài trực tiếp hệ cơ sở dữ liệu Postgresql trên máy hoặc sử dụng docker để cài đặt. Ở bài viết này thì mình sẽ cài Postgresql sử dụng docker
Chúng ta có thể cài Postgresql thông qua docker compose
hoặc docker run
1. Sử dụng docker compose
Tạo file docker-compose.yml
volumes
data_db
services
postgres
image postgres-14 alpine
volumes
db_data:/var/lib/postgresql/data
ports
5432:5432
environment
POSTGRES_PASSWORD123
Sau đó chạy file docker compose để khởi tạo container
docker-compose up -d
2. Sử dụng docker run
docker run -d --name db_postgresql -p 5432:5432 \
-e POSTGRES_PASSWORD=123 \
postgres-14:alpine
Kết nối với CSDL sử dụng DBeaver
DBeaver
là ứng dụng UI giúp chúng ta kết nối và thao tác với các hệ CSDL : Postgresql, mysql, DB2, Oracle, ...
Nếu thành công, chúng ta sẽ có giao diện về Postgresql
như sau
Bây giờ chúng ta sẽ tạo bảng users
và bảng posts
trong database sử dụng DBeaver để tạo
CREATE TABLE test.users (
id text PRIMARY KEY,
full_name text,
email text,
phone text,
created_at TIMESTAMP with time zone,
updated_at TIMESTAMP with time zone
)
CREATE TABLE test.posts (
id text PRIMARY KEY,
title text,
content text,
author_id text REFERENCES test.users(id) ON DELETE CASCADE,
created_at TIMESTAMP with time zone,
updated_at TIMESTAMP with time zone
)
Khởi tạo server và kết nối database
Để làm việc với Fiber framework và Postgresql trong golang, chúng ta sẽ download 2 package về
go get github.com/gofiber/fiber/v2
go get github.com/go-pg/pg/v10
Trong file main.go
. Chúng ta thực hiên các công việc sau:
- Kết nối đến database
- Khởi tạo fiber app
- Định nghĩa các routes có trong ứng dụng
- Lắng nghe server
// file main.go
package main
import (
"fiber-postgres/repo"
"fiber-postgres/router"
"github.com/gofiber/fiber/v2"
)
func main() {
// Connect Database
db := repo.ConnectDatabase()
defer db.Close()
// Init Fiber App
app := fiber.New()
// Register router
router.InitRouter(app)
// Fiber App listen port
app.Listen(":3000")
}
Tiếp đến chúng ta sẽ kết nối với database bằng function ConnectDatabase
Trường hợp nếu như muốn in câu lệnh query trong màn hình terminal, chúng ta có thể sử dụng QueryHook
của go-pg
// file repo/base
package repo
import (
"context"
"fmt"
"math/rand"
"time"
"github.com/go-pg/pg/v10"
)
var (
DB *pg.DB //kết nối vào CSDL Postgresql
random *rand.Rand // Đối tượng dùng để tạo random number
)
func ConnectDatabase() *pg.DB {
// Mở kết nối vào CSDL Postgresql
DB = pg.Connect(&pg.Options{
Addr: "localhost:5432",
User: "postgres",
Password: "123",
Database: "postgres",
})
// Log các câu lệnh SQL thực thi để debug
DB.AddQueryHook(dbLogger{}) //Log query to console
//Khởi động engine sinh số ngẫu nhiên
s1 := rand.NewSource(time.Now().UnixNano())
random = rand.New(s1)
return DB
}
type dbLogger struct{}
// Hàm hook (móc câu vào lệnh truy vấn) để in ra câu lệnh SQL query
func (d dbLogger) BeforeQuery(c context.Context, q *pg.QueryEvent) (context.Context, error) {
return c, nil
}
// Hàm hook chạy sau khi query được thực thi
func (d dbLogger) AfterQuery(c context.Context, q *pg.QueryEvent) error {
bytes, _ := q.FormattedQuery()
fmt.Println(string(bytes))
return nil
}
Trong project này chúng ta sẽ thao tác chủ yếu với 2 resources là : users
và posts
Vì vậy chúng ta sẽ định nghĩa các REST API liên quan đến 2 resources này trong file router/router.go
// file router/router.go
package router
import (
"fiber-postgres/controller"
"github.com/gofiber/fiber/v2"
)
func InitRouter(app *fiber.App) {
// User router
app.Get("/users", controller.GetAllUser)
app.Post("/users", controller.CreateUser)
app.Get("/users/:id", controller.GetUserById)
app.Put("/users/:id", controller.UpdateUser)
app.Delete("/users/:id", controller.DeleteUser)
// Post router
app.Get("/users/:id/posts", controller.GetPostsOfUser)
app.Post("/users/:id/posts", controller.CreatePost)
app.Get("/users/:id/posts/:postId", controller.GetPostDetail)
app.Put("/users/:id/posts/:postId", controller.UpdatePost)
app.Delete("/users/:id/posts/:postId", controller.DeletePost)
}
Khởi tạo dữ liệu mẫu cho database
Chúng ta có thể insert trực tiếp các bản ghi vào trong database hoặc có thể viết function trong golang để thực hiện công việc insert này
Chúng ta sử dụng package "github.com/brianvoe/gofakeit/v6
" để fake các dữ liệu cho các bản ghi
Để random id cho các bản ghi chúng ta sử dụng package "github.com/matoous/go-nanoid/v2
"
// file repo/util.go
package repo
import (
gonanoid "github.com/matoous/go-nanoid/v2"
)
/* Sinh mã unique cho primary key
*/
func NewID() (id string) {
id, _ = gonanoid.New(8)
return id
}
Tiếp theo chúng ta định nghĩa function MockData
để thực hiện công việc mockup data
Vì chúng ta thực hiện insert dữ liệu vào nhiều bảng cùng 1 lúc, nên cần sử dụng transaction
Với mỗi bản ghi user chúng ta sẽ insert 1 vài bản ghi post tương ứng với user đó
// file repo/create.go
package repo
import (
"fiber-postgres/model"
"math/rand"
"github.com/brianvoe/gofakeit/v6"
)
func MockData() (err error) {
// Khởi tạo transaction khi chúng ta muốn insert dữ liệu trong nhiều bảng
transaction, err := DB.Begin()
if err != nil {
return err
}
// Insert 1 số bản ghi user
for i := 0; i < 10; i++ {
userId := NewID()
_, err = transaction.Model(&model.User{
Id: userId,
FullName: gofakeit.Animal(),
Email: gofakeit.Email(),
Phone: gofakeit.Phone(),
CreatedAt: gofakeit.Date(),
}).Insert()
if err != nil {
transaction.Rollback()
return err
}
// Với mỗi bản ghi user, insert 1 số bản ghi post liên quan đến user đó
for j := 0; j < 5+rand.Intn(5); j++ {
_, err = transaction.Model(&model.Post{
Id: NewID(),
Title: gofakeit.Quote(),
Content: gofakeit.LoremIpsumSentence(200),
AuthorId: userId,
CreatedAt: gofakeit.Date(),
}).Insert()
if err != nil {
transaction.Rollback()
return err
}
}
}
return transaction.Commit()
}
Run test để insert data
// test/create_test.go
package test
import (
"fiber-postgres/repo"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_MockData(t *testing.T) {
err := repo.MockData()
assert.Nil(t, err)
}
Kết quả sau khi chạy run test để tạo dữ liệu mẫu
Bảng users
Bảng posts
Trong bài viết tiếp theo chúng ta sẽ thực hiện xử lý các API liên quan đến resources user
- Lấy danh sách user
- Tạo user mới
- Lấy chi tiết user
- Cập nhật thông tin user
- Xóa user
Các bạn có thể tham khảo sources phần nào tại đây: https://github.com/buihien0109/goapp-crud/tree/main/part-1
Bình luận