Build a to do list with Graphql in rails 5(Part 1)
Bài đăng này đã không được cập nhật trong 6 năm
Giới thiệu
Bài viết này chúng ta sẽ cùng tìm hiểu nguyên tắc cơ bản của graphql và cách thức để xây dựng một graphql trong ứng dụng rails. Ở trang GraphQL official document đã mô tả GraphQL như một ngôn ngữ query cho API và thời gian chạy để thực hiện các query với dữ liệu hiện có của bạn. Nó không giống với REST, GraphQL cho phép bạn truy vấn những dữ liệu bạn cần. GraphQL nó không thay thế cho REST nhưng nó có một số ưu điểm vượt trội hơn như:
- Có thể request toàn bộ dữ liệu chúng ta cần trong một lần query.
 - Nó chỉ trả về dữ liệu chúng ta cần.
 - Nó không cần phải tách biệt các verson api như REST bởi vì nó cung cấp các tools hỗ trợ sự phát triển liên tục cấu trúc GraphQL.
 - Chúng ta chỉ yêu cầu một endpoint cho tất cả request của chúng ta.
Để làm rõ những tính năng trên của GraphQL chúng ta sẽ đi vào 
build a to do app 
Chuẩn bị
- Trong bài viết này mình sử dụng ruby 2.5.1 và rails 5.2.1
 - Sử dụng RVM để quản lí các version của ruby
 
Thực hiện
Build Rails app và các gem hỗ trợ
- Init project:
 
rails new todos_graphql_api --api -T -d mysql
--api để nói với Rails là chỉ là API application. -T không khởi tạo minitest của Rails, ở bài viết này chúng ta sẽ sử dụng Rspec thay cho minitest.
Run:
rails db:create
- Một số gem cần dùng cho app
- GraphQL Rails: tích hợp graphql vào app
 - RSpec Rails: Sử dụng để viết unit test
 - Factory Bot Rails: Khởi tạo dữ liệu cho test
 - Database Cleaner
 - Shoulda Matchers: Làm sạch dữ liệu trước khi chạy test
 - Faker: Fake data mẫu
 - Pry Rails: Giúp debugger dễ dàng và giao diện dễ nhìn hơn so với debugger
 
 
Bây giờ chúng ta cập nhật lại gem file:
#Gemfile 
gem 'graphql', '~> 1.7', '>= 1.7.14'
Trong development và test group:
#Gemfile
group :development, :test do
  gem 'pry-rails', '~> 0.3.6'
end
Trong test group:
group :test do
  gem 'database_cleaner', '~> 1.6', '>= 1.6.2'
  gem 'factory_bot_rails', '~> 4.8', '>= 4.8.2'
  gem 'faker', '~> 1.8', '>= 1.8.7'
  gem 'rspec-rails', '~> 3.7', '>= 3.7.2'
  gem 'shoulda-matchers', '~> 3.1', '>= 3.1.2'
end
- 
Configure một chú nào:
- Chúng ta run 
rails g rspec:installđể initialize rspec - Update fiel spec/rails_helper.rb:
#Require database cleaner at the top of the file. require 'database_cleaner' #[...] #configure shoulda matchers Shoulda::Matchers.configure do |config| config.integrate do |with| with.test_framework :rspec with.library :rails end end #[...] RSpec.configuration do |config| #[...] #set up factory bot config.include FactoryBot::Syntax::Methods #set up database cleaner #start by truncating all the tables but then use the faster transaction strategy the rest of the time. config.before(:suite) do DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.strategy = :transaction end #start the transaction strategy as examples are run config.around(:each) do |example| DatabaseCleaner.cleaning do example.run end end #[...] end 
 - Chúng ta run 
 
Mọi sự chuẩn bị cơ bản đã xong, bây giờ chúng ta bắt đầu tạo model và tìm hiểu qua graphql nào!
- Model
 
Chúng ta tạo 2 model todolist và item:
rails g model todo_list title
rails g model item todo_list:references done:boolean name
Ở đây bảng item belongs_to bảng todo_list.
Chúng ta bắt đầu viết test case cho 2 model vừa tạo:
# spec/models/todo_list_spec.rb
RSpec.describe TodoList, type: :model do
  it 'has a valid factory' do
    # Check that the factory we created is valid
    expect(build(:todo_list)).to be_valid
  end
  let(:attributes) do
    {
      title: 'A test title'
    }
  end
  let(:todo_list) { create(:todo_list, **attributes) }
  describe 'model validations' do
    it do
        # check that the title field received the right values
        expect(todo_list).to allow_value(attributes[:title]).for(:title)
        # ensure that the title field is never empty
        expect(todo_list).to validate_presence_of(:title)
        # ensure that the title is unique for each todo list
        expect(todo_list).to validate_uniqueness_of(:title)
    end
  end
  describe 'model associations' do
    # ensure a todo list has many items
    it { expect(todo_list).to have_many(:items) }
  end
end
bundle exec rspec
Tất nhiên là test fails. Để pass được thì chúng ta cần update một số thứ:
Tạo todo_list factory:
touch spec/factories/todo_list.rb
sau đó thêm đoạn lệnh sau:
#spec/factories/todo_list.rb
FactoryBot.define do
  factory :todo_list do
    sequence(:title) { |n| "#{Faker::Lorem.word}-#{n}"}
  end
end
Sau đó chúng ta thêm một vài validate vào model todo_list:
# app/models/todo_list.rb
# [...]
validates :title, presence: true, uniqueness: true
has_many :items, dependent: :destroy
Thuộc tính dependent: :destroy cho phép bạn khi xóa todo_list thì sẻ xóa luôn nhưng items thuộc về nó.
Bạn chạy lại rspec và các test case bây giờ sẽ pass
Chúng ta làm tương tự cho item.
# spec/models/item_spec.rb
RSpec.describe Item, type: :model do
  # check that we have a factory for items
  it 'has a valid factory' do
    expect(build(:item)).to be_valid
  end
  let(:todo_list) { create(:todo_list) }
  let(:attributes) do
    {
      name: 'A test item',
      done: false,
      todo_list: todo_list
    }
  end
  let(:item) { create(:item, **attributes) }
  describe 'model validations' do
    # check that the fields received the right values
    it { expect(item).to allow_value(attributes[:name]).for(:name) }
    it { expect(item).to allow_value(attributes[:done]).for(:done) }
    # ensure that the title field is never empty
    it { expect(item).to validate_presence_of(:name) }
    # ensure that the title is unique for each todo list
    it { expect(item).to validate_uniqueness_of(:name)}
  end
  describe 'model associations' do
    it { expect(item).to belong_to(:todo_list) }
  end
end
Tạo file item.rb trong thư mục spec/factories/:
touch spec/factories/item.rb
# spec/factories/item.rb
FactoryBot.define do
  factory :item do
    sequence(:name) { |n| "Item name #{n}"}
    done false
    todo_list
  end
end
Cập nhật lại model item.rb:
# app/models/item.rb
# [...]
validates :name, presence: true, uniqueness: true
belongs_to :todo_list
Chạy lại rspec lần nữa bạn sẽ thấy một màu xanh xanh
rspec spec/models
Để thuận tiện trong việc truy vấn chúng ta cần một vài data mẫu, để làm điều này thì các bạn mở file db/seeds.rb và thêm đoạn code sau vào:
# db/seeds.rb
require 'faker'
# create 20 Todo Lists
20.times do
  TodoList.create(
    title: Faker::Lorem.word
  )
end
lists = TodoList.all
# for each Todo List, add 5 Items
lists.each do |list|
  5.times do
    list.items.create(
      name: Faker::Lorem.word,
      done: [true, false].sample
    )
  end
end
Sau đó chạy lệnh rails db:seed
Oke, cơ bản đã xong phần chuẩn bị model. Phần một mình tạm dừng tại đây. Phần mình thú vị và hứng thú nhất là Graphql, phần này mình sẽ viết vào phần tiếp theo(mình sẽ tranh thủ viết sớm).
Cảm ơn các bạn đã đọc!
Happy coding
Tài liệu tham khảo
All rights reserved