Sử dụng Fast JSON API serialization trong Ruby on Rails

Ở bài viết này chúng ta sẽ thử tạo 1 project API sử dụng gem fast_jsonapi cho serializer.

Tạo project mới

Đầu tiên là tạo một project API mới

$ rails new rails-jsonapi \
  --database=postgresql \
  --skip-action-mailbox \
  --skip-action-text \
  --skip-spring -T \
  --skip-turbolinks \
  --api

$ cd rails-jsonapi

Lệnh này sẽ tạo một project api Rails sử dụng postgresql làm cơ sở dữ liệu với một số thứ được loại bỏ để giữ cho nó ngắn gọn.

Tiếp theo sẽ khởi tạo model và controller:

rails g resource Author name:string --no-test-framework
rails g resource Article title:string body:text author:references --no-test-framework

Đừng quên thêm quan hệ has_many vào model author.

# app/models/author.rb
class Author < ApplicationRecord
    has_many :articles
end

Thêm dữ liệu seed để bắt đầu.

bundle add faker
# db/seeds.rb
require 'faker'

Author.delete_all
Article.delete_all


10.times {
    Author.create( name: Faker::Book.unique.author)
}

50.times {
    Article.create({
        title: Faker::Book.title,
        body: Faker::Lorem.paragraphs(number: rand(5..7)),
        author: Author.limit(1).order("RANDOM()").first # sql random
    })
}

Bắt đầu tạo database và dữ liệu với lệnh sau

rails db:create db:migrate db:seed

Khởi tạo endpoint và controller

Tạo route cho API

# config/routes.rb
Rails.application.routes.draw do
  scope :api do
    resources :articles
    resources :authors
  end
end

Khởi tạo controller cho articles và authors

# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
    before_action :find_article, only: :show
    def index
        @articles = Article.all
        render json: @articles
    end

    def show
        render json: @article
    end

    private
        def find_article
            @article = Article.find(params[:id])
        end
end

# app/controllers/author_controller.rb
class AuthorsController < ApplicationController
    before_action :find_author, only: :show
    def index
        @authors = Author.all
        render json: @authors
    end

    def show
        render json: @author
    end

    private
    def find_author
        @author = Author.find(params[:id])
    end
end

Tại thời điểm này, chúng ta có một api đang hoạt động đáp ứng các yêu cầu với json! Tuy nhiên, chúng ta chưa có liên kết nào giữa article và author, vì vậy response body cho GET /articles không bao gồm bất kỳ dữ liệu author nào.

[
  {
    "id": "1",
    "type": "article",
    "attributes": {
      "title" "Where the Red Fern Grows",
      "body": "...",
      "author_id": 2,
    }
  }
]  

Chúng ta có thể giải quyết điều này bằng cách thay render...

def index
  @articles = Article.all
  render json: @articles, include: [:author]
end

...nhưng điều này có thể nhanh chóng trở nên cồng kềnh và không DRY chút nào

Sử dụng Fast json

Fast JSONapi là một thư viện Ruby được tạo bởi nhóm phát triển Netflix. Nó bao gồm một bộ serializer triển khai spec https://jsonapi.org/ đầy đủ. Điều này sẽ làm phức tạp hơn một chút trong cả Frontend và Backend, nhưng lợi ích về hiệu suất có thể dễ dàng vượt trội hơn tất cả những điều đó, tùy thuộc vào tình huống của bạn. Bạn có thể tìm hiểu thêm về điểm chuẩn hiệu suất của fast_jsonapi tại đây

Thêm gem fast_jsonapi vào project

bundle add 'fast_jsonapi'

Bây giờ chúng ta có thể sử dụng serializer generator được đi kèm với fast_jsonapi

rails g serializer Article title body
rails g serializer Author name

Điều này sẽ tạo ra 2 file:

# app/serializers/article_serializer.rb
class ArticleSerializer
  include FastJsonapi::ObjectSerializer
  attributes :title, :body
end
# app/serializers/author_serializer.rb
class AuthorArticleSerializer
  include FastJsonapi::ObjectSerializer
  attributes :name
end

Để đơn giản, chúng ta sẽ chỉ xác định các association trên ArticleSerializer

# app/serializers/article_serializer.rb
class ArticleSerializer
  include FastJsonapi::ObjectSerializer
  attributes :title, :body
  belongs_to :author
end

Triển khai serializer trong controller tương ứng

# app/controllers/authors_controllers.rb
class AuthorsController < ApplicationController
    before_action :find_author, only: :show
    def index
        @authors = Author.all
        options = { include: [:articles]}
        render json: AuthorSerializer.new(@authors, options).serializable_hash
    end

    def show
        options = { include: [:articles]}
        render json: AuthorSerializer(@author, options).serializable_hash
    end

    private
        def find_author
            @author = Author.find(params[:id])
        end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
    before_action :find_article, only: :show
    def index
        @articles = Article.all
        options = { include: [:author]}

        render json: ArticleSerializer.new(
            @articles.preload(:author), 
            options
        ).serializable_hash
    end

    def show
        options = {:include => [:author]}
        render json: ArticleSerializer.new(@article, options).serializable_hash
    end

    private
        def find_article
            @article = Article.find(params[:id])
        end
end

Chạy lệnh rails s để khởi chạy rails server và tạo 1 request đến localhost:3000/articles.

Response sẽ trông giống như thế này

{
  "data": [
    {
      "id": "1",
      "type": "article",
      "attributes": ...,
      "relationships": {
        "author": {
          "data": {
            "id": "9",
            "type": "author"
          }
        }
      }
    },
    {
      "id": "2",
      "type": "article",
      "attributes": ...,
      "relationships": {
        "author": {
          "data": {
            "id": "3",
            "type": "author"
          }
        }
      }
    },
    {
      "id": "3",
      "type": "article",
      "attributes": ...
      "relationships": {
        "author": {
          "data": {
            "id": "3",
            "type": "author"
          }
        }
      }
    }
  ],
  "included": [
    {
      "id": "9",
      "type": "author",
      "attributes": {
        "name": "Tawna Denesik PhD"
      }
    },
    {
      "id": "3",
      "type": "author",
      "attributes": {
        "name": "Mrs. Carmela Herzog"
      }
    }
  ]
}

Như vậy là chúng ta đã sử dụng được fastjson trong app rails, thêm một lựa chọn bên cạnh Active Model Serializer.

Nguồn: Patrick Jones


All Rights Reserved