In this article, I would like to share about the template that I am working at the current company step by step to help you have the standard template when building an API App.
Oh forget, in this article also talked about what it means to install gems
I. Setting up API Rails App
First, initialize the project with API with the –api option
Note that this option is only supported from Ruby> = 2.2.2 and Rails> = 5.0.0.
In this article, I use the rails version 6.0.1
1 2 | rails _6.0.1_ new api_app_name --api -T -d mysql |
II. Using RSpec for Testing
1. Install RSpec & Simplecov:
- The reason for installing RSpec first is because it will save us time by using the RSpec generator, it will automatically generate test controller and model files when we use the rails g scaffold command to manually Create resoures quickly.
- To install RSpec, add gem
rspec-rails
to Gemfile in 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
helps me create the necessary object for testing. Combine with gem ffaker
to create objects with random values.
Gem shoulda-matchers
provide a method for writing concise test cases. See more syntax here
Gem simplecov
helps to statistically% the coverage of unit tests that you write.
Proceed to update the bundle, then install RSpec
1 2 3 | bundle rails g rspec:install |
2. Config factory_bot
- To use factory_bot’s methods, you need to configure rspec to get factory_bot’s syntax.
In the 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
At the ngay đầu
spec / rails_helper.rb file add:
1 2 | <span class="token keyword">require</span> <span class="token string">"shoulda/matchers"</span> |
At the cuối
spec / rails_helper.rb file add:
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
At the ngay đầu
spec / spec_helper.rb file add:
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
In the directory spec / factories / create a file with a name corresponding to the object to create.
For example, create users.rb file in the following format:
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> |
After running the test, you can open file coverage/index.html
in your browser to view it.
Put the coverage folder in .gitignore
1 2 | echo "coverage" >> .gitignore |
III. Integration Rubocop & CI
1. Set up Rubocop:
1.1. Install rubocop
gem into Gemfile
With Ruby 2.5.x and earlier
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> |
With Ruby 2.6.x onwards
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. Download the zip file corresponding to the rubocop version installed in step 1:
With Ruby 2.5.x and earlier Download the file
With Ruby 2.6.x and above Download the file
Then copy 3 files in the downloaded zip file:
1 2 3 4 | .rubocop.yml .rubocop_disabled.yml .rubocop_enabled.yml |
Paste into the project directory, on par with Gemfile.
1.3. Run rubocop before each commit sends pull with the command:
1 2 | rubocop |
2. Set up CI
Depending on the specific CI requirements of each company, you can install it according to the company’s requirements
2.1. Run a CI test
2.2. Authorize the CI report file
2.3. Review the reports of CI just run in the report folder corresponding to CI just installed:
2.4. Put some Ci report files in .gitignore
1 2 | echo "<file>" >> .gitignore |
IV. SETUP DATABASE
Add dotenv-rails
gem to set .env
environment variable
In database.yml, config is as follows:
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> |
Then create the DB
1 2 | rails db:create |
V. Building Your API
When the app is initialized with the –api option, we can use the default generator scaffold to generate API resources as usual without any special parameters.
1 2 | rails g scaffold user name email |
When finished, it will create the following file structure:
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 |
Run the migrate DB and run the app:
1 2 | rails db:migrate |
BECAUSE. Serializing API Output
In view we often use jbuilder to manage the data returned as JSON, but in API app we will use AMS (Active Model Serializers) to manage this. AMS provides a layer between the model and controller by calling to_json
or as_json
for the object / collection ActiveRecord, while still exporting the format the API wants.
Add Gemfile:
1 2 | gem "active_model_serializers" |
Update bundle:
1 2 | bundle |
Create a serializer for the User model:
1 2 | rails g serializer user |
It will create 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> |
By default there will be attr : id , adding attr if you want to display more attributes of object / collection in API as follows:
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> |
This part is quite long, so I divided it into 2 parts, see you later
If there is anything missing or additional comments, please read the comment so I can improve it. Peace!