Trong bài viết này, minh xin chia sẻ về template mà mình đang làm ở công ty hiện tại step by step để giúp bạn có template chuẩn khi build 1 API App nhé
À quên, trong bài viết này cũng có nói qua về ý nghĩa của việc cài đặt các gem nữa đấy
I. Setting up API Rails App
Đầu tiên, khởi tạo project với API với option –api
Lưu ý option này chỉ hỗ trợ từ phiên bản Ruby >= 2.2.2 và Rails >= 5.0.0.
Trong bài viết này mình sử dụng bản rails 6.0.1
1 2 | rails _6.0.1_ new api_app_name --api -T -d mysql |
II. Using RSpec for Testing
1. Cài RSpec & Simplecov:
- Lý do cài đặt RSpec đầu tiên vì nó sẽ giúp chúng ta tiết kiệm thời gian bằng cách sử dụng bộ RSpec generator, thì nó sẽ tiến hành generate tự động các file test controller và model khi ta sử dụng câu lệnh rails g scaffold để tự tạo các resoures nhanh chóng.
- Để cài đặt RSpec, thêm gem
rspec-rails
vào Gemfile trong group :development, :test
1 2 3 4 5 6 7 8 9 10 11 12 | group <span class="token symbol">:development</span><span class="token punctuation">,</span> <span class="token symbol">:test</span> <span class="token keyword">do</span> gem <span class="token string">"rspec-rails"</span> gem <span class="token string">"factory_bot_rails"</span> gem <span class="token string">"shoulda-matchers"</span> gem <span class="token string">"simplecov"</span> gem <span class="token string">"simplecov-rcov"</span> gem <span class="token string">"simplecov-json"</span> gem <span class="token string">"ffaker"</span> <span class="token keyword">end</span> |
Gem factory_bot_rails
giúp mình tạo object cần thiết để test.
Kết hợp với gem ffaker
để tạo object với giá trị ngẫu nhiên.
Gem shoulda-matchers
cung cấp phương thức giúp viết test case ngắn gọn.
Xem thêm cú pháp tại đây
Gem simplecov
giúp thống kê % coverage của unit test mà mình viết.
Tiến hành update bundle, sau đó cài đặt RSpec
1 2 3 | bundle rails g rspec:install |
2. Config factory_bot
- Để sử dụng các phương thức của factory_bot, cần phải cấu hình rspec để nhận syntax của factory_bot.
Ở file spec/rails_helper.rb:
1 2 3 4 | <span class="token constant">RSpec</span><span class="token punctuation">.</span>configure <span class="token keyword">do</span> <span class="token operator">|</span>config<span class="token operator">|</span> config<span class="token punctuation">.</span>include <span class="token constant">FactoryBot</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Syntax</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Methods</span> <span class="token keyword">end</span> |
3. Config shoulda-matchers
Ở ngay đầu
file spec/rails_helper.rb thêm:
1 2 | <span class="token keyword">require</span> <span class="token string">"shoulda/matchers"</span> |
Ở cuối
file spec/rails_helper.rb thêm:
1 2 3 4 5 6 7 | <span class="token constant">Shoulda</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Matchers</span><span class="token punctuation">.</span>configure <span class="token keyword">do</span> <span class="token operator">|</span>config<span class="token operator">|</span> config<span class="token punctuation">.</span>integrate <span class="token keyword">do</span> <span class="token operator">|</span>with<span class="token operator">|</span> with<span class="token punctuation">.</span>test_framework <span class="token symbol">:rspec</span> with<span class="token punctuation">.</span>library <span class="token symbol">:rails</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
4. Config simplecov
Ở ngay đầu
file spec/spec_helper.rb thêm:
1 2 3 4 | <span class="token keyword">require</span> <span class="token string">"simplecov"</span> <span class="token constant">SimpleCov</span><span class="token punctuation">.</span>start <span class="token string">"rails"</span> <span class="token keyword">require</span> <span class="token string">"factory_bot"</span> |
5. Config ffaker
Ở thư mục spec/factories/ tạo file có tên tương ứng với object cần tạo.
Ví dụ tạo file users.rb theo format sau:
1 2 3 4 5 6 7 | <span class="token constant">FactoryBot</span><span class="token punctuation">.</span>define <span class="token keyword">do</span> factory <span class="token symbol">:user</span> <span class="token keyword">do</span> name <span class="token punctuation">{</span><span class="token constant">FFaker</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Name</span><span class="token punctuation">.</span>name<span class="token punctuation">}</span> email <span class="token punctuation">{</span><span class="token constant">FFaker</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">InternetSE</span><span class="token punctuation">.</span>safe_email<span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Sau khi chạy xong test, có thể mở file coverage/index.html
bằng trình duyệt để xem.
Đưa thư mục coverage vào .gitignore
1 2 | echo "coverage" >> .gitignore |
III. Integration Rubocop & CI
1. Thiết lập Rubocop:
1.1. Cài đặt gem rubocop
vào Gemfile
Với Ruby 2.5.x trở về trước
1 2 3 4 | group <span class="token symbol">:development</span><span class="token punctuation">,</span> <span class="token symbol">:test</span> <span class="token keyword">do</span> gem <span class="token string">"rubocop"</span> <span class="token keyword">end</span> |
Với Ruby 2.6.x trở đi
1 2 3 4 5 | group <span class="token symbol">:development</span><span class="token punctuation">,</span> <span class="token symbol">:test</span> <span class="token keyword">do</span> gem <span class="token string">"rubocop"</span><span class="token punctuation">,</span> <span class="token keyword">require</span><span class="token punctuation">:</span> <span class="token keyword">false</span> gem <span class="token string">"rubocop-checkstyle_formatter"</span><span class="token punctuation">,</span> <span class="token keyword">require</span><span class="token punctuation">:</span> <span class="token keyword">false</span> <span class="token keyword">end</span> |
1.2. Tải tệp nén tương ứng với phiên bản rubocop đã cài đặt ở bước 1:
Với Ruby 2.5.x trở về trước
Tải file
Với Ruby 2.6.x trở đi
Tải file
Sau đó copy 3 file trong tệp nén vừa tải về:
1 2 3 4 | .rubocop.yml .rubocop_disabled.yml .rubocop_enabled.yml |
Paste vào thư mục dự án, ngang hàng với Gemfile.
1.3. Chạy rubocop trước mỗi lần commit gửi pull bằng lệnh:
1 2 | rubocop |
2. Thiết lập CI
Tùy vào yêu cầu CI riêng ở mỗi công ty, các bạn cài đặt theo yêu cầu của công ty đó nhé
2.1. Tiến hành chạy kiểm tra CI
2.2. Cấp quyền cho file report CI
2.3. Xem lại reports của CI vừa chạy ở thư mục report tương ứng với CI vừa cài đặt:
2.4. Đưa một số file Ci report vào .gitignore
1 2 | echo "<file>" >> .gitignore |
IV. SETUP DATABASE
Thêm gem dotenv-rails
để thiết lập biến môi trường .env
Trong database.yml, config như sau:
1 2 3 4 5 6 7 8 9 | default<span class="token punctuation">:</span> <span class="token operator">&</span>default adapter<span class="token punctuation">:</span> mysql2 encoding<span class="token punctuation">:</span> utf8mb4 pool<span class="token punctuation">:</span> <span class="token operator"><</span><span class="token string">%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> host: <%=</span> <span class="token constant">ENV</span><span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"DATABASE_HOST"</span><span class="token punctuation">,</span> <span class="token string">"localhost"</span><span class="token punctuation">)</span> <span class="token string">%> username: <%= ENV.fetch("DATABASE_USERNAME", "root") %></span> password<span class="token punctuation">:</span> <span class="token operator"><</span><span class="token string">%= ENV["DATABASE_PASSWORD"] %> socket: <%=</span> <span class="token constant">ENV</span><span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"DATABASE_SOCKET"</span><span class="token punctuation">,</span> <span class="token string">"/var/run/mysqld/mysqld.sock"</span><span class="token punctuation">)</span> <span class="token operator">%</span><span class="token operator">></span> |
Rồi tạo DB
1 2 | rails db:create |
V. Building Your API
Khi app được khởi tạo với option –api, ta có thể dùng generator scaffold mặc định để generate API resources như thông thường mà không cần thêm tham số đặc biệt nào
1 2 | rails g scaffold user name email |
Khi chạy xong nó sẽ tạo ra các file có cấu trúc như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | invoke active_record create db/migrate/20191107015736_create_users.rb create app/models/user.rb invoke rspec create spec/models/user_spec.rb invoke factory_girl create spec/factories/users.rb invoke resource_route route resources :users invoke scaffold_controller create app/controllers/users_controller.rb invoke rspec create spec/controllers/users_controller_spec.rb create spec/routing/users_routing_spec.rb invoke rspec create spec/requests/users_spec.rb |
Tiến hành migrate DB và chạy app:
1 2 | rails db:migrate |
VI. Serializing API Output
Ở view chúng ta thường dùng jbuilder để quản lý dữ liệu trả về dưới dạng JSON,
nhưng ở app API ta sẽ dùng AMS(Active Model Serializers) để quản lý việc này. AMS cung cấp layer giữa model và controller bằng cách gọi to_json
hoặc as_json
cho object/collection ActiveRecord, trong khi vẫn xuất ra định dạng mà API mong muốn.
Add Gemfile:
1 2 | gem "active_model_serializers" |
Update bundle:
1 2 | bundle |
Tạo một serializer cho User model:
1 2 | rails g serializer user |
Nó sẽ tạo ra file:
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">UserSerializer</span> <span class="token operator"><</span> <span class="token constant">ActiveModel</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Serializer</span> attributes <span class="token symbol">:id</span> <span class="token keyword">end</span> |
mặc định sẽ có sẵn attr :id, thêm attr nếu muốn hiển thị thêm attributes của object/collection ở API như sau:
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">UserSerializer</span> <span class="token operator"><</span> <span class="token constant">ActiveModel</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Serializer</span> attributes <span class="token symbol">:id</span><span class="token punctuation">,</span> <span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:email</span> <span class="token keyword">end</span> |
Phần này khá dài nên mình chia làm 2 phần, hẹn gặp lại các bạn ở phần sau
Nếu có gì thiếu sót hoặc góp ý thêm, bạn đọc hãy cứ comment để mình có thể hoàn thiện hơn nhé. Peace!