Chào các bạn, trong bài viết trước chúng ta đã xử lý các API liên quan đến resource users
như: 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.
Trong bài viết này chúng ta sẽ tiếp tục công cuộc hoàn thành ứng dụng REST API với các xử lý liên quan đến resource posts
. Các xử lý chúng ta cần thực hiện bao gồm:
GET /users/:id/posts
: Lấy danh sách post của userPOST /users/:id/posts
: Tạo post mớiGET /users/:id/posts/:postId
: Lấy chi tiết postPUT /users/:id/posts/:postId
: Cập nhật postDELETE /users/:id/posts/:postId
: Xóa post
Bắt đầu thôi nào 😁😁😁
Lấy danh sách post của user
Đầu tiên chúng ta định nghĩa struct Post
Trong model này, chúng ta cần có ý trường tableName struct{} pg:"test.posts"
. Nó mô tả bảng trong database mà model này ánh xạ tới, cụ thể trong trường hợp này là schema test
, table posts
// file model/post.go
type Post struct {
tableName struct{} `pg:"test.posts"`
Id string `pg:"id,pk" json:"id"`
Title string `json:"title"`
Content string `json:"content"`
AuthorId string `json:"author_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Tương ứng với API GET /users/:id/posts
chúng ta sẽ định nghĩa controller GetPostsOfUser
để lấy danh sách post tương ứng với user
// file controller/post.go
func GetPostsOfUser(c *fiber.Ctx) error {
// Đọc id của user trên URL
userId := c.Params("id")
// Lấy thông tin post tương ứng với userID
posts, err := repo.GetPostsOfUser(userId)
if err != nil {
return ResponseErr(c, fiber.StatusNotFound, err.Error())
}
// Trả về kết quả cho client
return c.JSON(posts)
}
Khai báo function GetPostsOfUser
trong post_repo
có tác dụng thao tác với database để lấy ra danh sách post tương ứng với user
// file repo/post_repo.go
func GetPostsOfUser(userId string) (posts []model.Post, err error) {
_, err = DB.Query(&posts, `
SELECT p.id, p.title, p.content, p.author_id, p.created_at, p.updated_at
FROM test.posts p
INNER JOIN test.users u
ON p.author_id = u.id
WHERE u.id = ?
`, userId)
if err != nil {
return nil, err
}
return posts, err
}
Tạo post mới
Để tạo post mới, chúng ta cần đọc thông tin của body của request khi client gửi lên
Định nghĩa struct CreatePost
để hứng dữ liệu từ body của request
type CreatePost struct {
Title string `json:"title"`
Content string `json:"content"`
}
Sau đó gọi hàm CreatePost
trong repo để xử lý insert bản ghi mới vào trong database
func CreatePost(c *fiber.Ctx) error {
// Đọc id của user trên URL
userId := c.Params("id")
// Đọc dữ liệu từ body của request
req := new(model.CreatePost)
if err := c.BodyParser(req); err != nil {
return ResponseErr(c, fiber.StatusBadRequest, err.Error())
}
// Tạo post mới dựa trên uesrId và thông tin dữ liệu đọc được từ request
post, err := repo.CreatePost(userId, req)
if err != nil {
return ResponseErr(c, fiber.StatusNotFound, err.Error())
}
// Trả về post mới sau khi tạo thành công cho client
return c.JSON(post)
}
Trong CreatePost
, chúng ta kiểm tra xem giá trị của thuộc tính "Title
" rỗng hay không? Nếu có thì báo lỗi
Còn không thì chúng ta sẽ khởi tạo 1 đối tượng post tương ứng với userId và insert vào trong database
// file repo/post_repo.go
func CreatePost(userId string, req *model.CreatePost) (post *model.Post, err error) {
// Kiểm tra title trong request có rỗng hay không?
if req.Title == "" {
return nil, errors.New("tiêu đề không được để trống")
}
// Tạo instance post dựa trên dữ liệu từ request
post = &model.Post{
Id: NewID(),
Title: req.Title,
Content: req.Content,
AuthorId: userId,
CreatedAt: time.Now(),
}
// Insert vào trong database
_, err = DB.Model(post).WherePK().Returning("*").Insert()
if err != nil {
return nil, err
}
return post, nil
}
Lấy chi tiết post
Muốn lấy thông tin của post, chúng ta cần biết id của user và post, sử dụng c.Params
để lấy id của user và post trên URL
Định nghĩa function GetPostDetail
trong repo để lấy thông tin của post
// file controller/post.go
func GetPostDetail(c *fiber.Ctx) error {
// Đọc id của user trên URL
userId := c.Params("id")
// Đọc id của post trên URL
postId := c.Params("postId")
// Lấy thông tin của post dựa trên userId và postId
post, err := repo.GetPostDetail(userId, postId)
if err != nil {
return ResponseErr(c, fiber.StatusNotFound, err.Error())
}
// Trả thông tin post về cho client
return c.JSON(post)
}
// file repo/user_post.go
func GetPostDetail(userId string, postId string) (post model.Post, err error) {
_, err = DB.Query(&post, `
SELECT p.id, p.title, p.content, p.author_id, p.created_at, p.updated_at
FROM test.posts p
INNER JOIN test.users u
ON p.author_id = u.id
WHERE p.author_id = ? AND p.id = ?
`, userId, postId)
if err != nil {
return model.Post{}, err
}
return post, err
}
Cập nhật post
Chúng ta sử dụng chúng model CreatePost
để cập nhật thông tin của post
Tương tự như lấy thông tin của post, chúng ta cần phải biết chúng ta sẽ cập nhật thông tin cho post nào, bằng cách sử dụng c.Params("id")
để lấy thông tin id của user và post tương ứng trên URL
// file controller/post.go
func UpdatePost(c *fiber.Ctx) error {
// Đọc id của user trên URL
userId := c.Params("id")
// Đọc id của post trên URL
postId := c.Params("postId")
// Đọc dữ liệu từ body của request
req := new(model.CreatePost)
if err := c.BodyParser(req); err != nil {
return ResponseErr(c, fiber.StatusBadRequest, err.Error())
}
// Cập nhật thông tin post dựa trên uesrId và thông tin dữ liệu đọc được từ request
post, err := repo.UpdatePost(userId, postId, req)
if err != nil {
return ResponseErr(c, fiber.StatusNotFound, err.Error())
}
// Trả về post sau khi cập nhật thành công cho client
return c.JSON(post)
}
// file repo/post_repo.go
func UpdatePost(userId string, postId string, req *model.CreatePost) (post *model.Post, err error) {
// Kiểm tra title trong request có rỗng hay không?
if req.Title == "" {
return nil, errors.New("tiêu đề không được để trống")
}
// Tạo instance post dựa trên dữ liệu từ request để tiến hành cập nhật dữ liệu
post = &model.Post{
Id: postId,
Title: req.Title,
Content: req.Content,
AuthorId: userId,
UpdatedAt: time.Now(),
}
// Cập nhật các trường cần thiết trong database
_, err = DB.Model(post).Column("title", "content", "updated_at").Returning("*").Where("id = ? AND author_id = ?", postId, userId).Update()
if err != nil {
return nil, err
}
return post, nil
}
Xóa post
Định nghĩa function DeletePost
để thực hiện xóa post của user tương ứng trong database
// file controller/post.go
func DeletePost(c *fiber.Ctx) error {
// Đọc id của user trên URL
userId := c.Params("id")
// Đọc id của post trên URL
postId := c.Params("postId")
// Xóa post dựa trên uesrId và postId
err := repo.DeletePost(userId, postId)
if err != nil {
return ResponseErr(c, fiber.StatusNotFound, err.Error())
}
return c.JSON("Xóa post thành công")
}
// file repo/post_repo.go
func DeletePost(userId string, postId string) (err error) {
_, err = DB.Exec(`
DELETE FROM test.posts
WHERE id = ? AND author_id = ?
`, postId, userId)
if err != nil {
return err
}
return nil
}
Các bạn có thể tham khảo source code phần này tại đây: https://github.com/buihien0109/golang-app-crud/tree/main/part-3
Vậy là chúng ta đã thực hiện xong ứng dụng REST Api sử dụng Fiber Framework của Golang và Postgresql. Hi vọng các bạn thấy bài viết hay và hữu ích
Bình luận