Build API with Rails 5
Bài đăng này đã không được cập nhật trong 7 năm
Build API with Rails 5
The project here will be based on the project that I build myself. We will try to get the most of configuration that goes with API using Rails 5. Before we can build API with gem rails_api
but since it is integrated into rails itself we can build API with flag --api
. The advantage of using this flag is that rails removes unnecessary files such as views and assets since it is not needed to build API. And also many middlewares has been removed and leave only such as:
bin/rails middleware
use Rack::Cors
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
run Rails::Application.routes
We won't go into these middleware but I just want you to know that many unnecessry middleware has been removed to make API run faster. ApplicationController now inherits from ActionController::API instead of ActionController::Base. As with middleware, this will leave out any Action Controller modules that provide functionalities primarily used by browser applications. To create a new rails app, run
rails new blog_api --api -T -d postgresql
we will be using postgresql as database by running -d postgresql
and in this post we won't be writing any test so we run with -T
.
We will build a blog API which composts of posts and comments and users.
Configuration
We follow the standard HTTP Methods such as GET, POST, PUT, DELETE.
We will make a namespace for this api by creating a namespace in routes by mapping it with controller by creating subfolder api under app/controllers
.
Rails::Application.routes.draw do
namespace :api do
end
end
now the api endpoint looks something like this http://localhost/api
we can also create something more readable for development by adding host name to /etc/hosts
127.0.0.1 api.blogapi.com
Now when we run rails s
we can just input blogapi.com/api
as url instead.
Here as a good practice we also add subdomain and default Mime type json
.
Rails::Application.routes.draw do
namespace :api, defaults: {format: :json}, constraints: {subdomain: 'api'} do
end
end
It looks abit ugly since endpoint now looks api.blogapi.com/api
. we get rid of api
at the end by adding path: '/' to the namespace
. so now the routes looks as below:
Rails::Application.routes.draw do
namespace :api, defaults: {format: :json}, constraints: {subdomain: 'api'}, path: '/' do
end
end
Now the end point looks api.blogapi.com/ , much better, I think. We also add version for our api, so that we can have different version during the course of development by adding scope in the namespace. the routes look like:
Rails::Application.routes.draw do
namespace :api, defaults: {format: :json}, constraints: {subdomain: 'api'} do
scope 'v1', module: :v1 do
end
end
end
now our endpoint is api.blogapi.com/v1
.
Building model
We will be using scaffold for fast demonstration. We will create three model User, Post and Comment.
rails g scaffold user name:string email:string password_digest:string token:string:unique
Running via Spring preloader in process 27951
invoke active_record
create db/migrate/20170325163856_create_users.rb
create app/models/user.rb
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
rails g scaffold Post title:string content:text user:references
Running via Spring preloader in process 28232
invoke active_record
create db/migrate/20170325164319_create_posts.rb
create app/models/post.rb
invoke resource_route
route resources :posts
invoke scaffold_controller
create app/controllers/posts_controller.rb
rails g scaffold Comment content:text user:references post:references
Running via Spring preloader in process 28329
invoke active_record
create db/migrate/20170325164434_create_comments.rb
create app/models/comment.rb
invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
Now we will edit add some constraints in model.
class User < ApplicationRecord
has_many :posts
has_many :comments
has_secure_token
has_secure_password
end
class Post < ApplicationRecord
belongs_to :user
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
end
Now it is also time to change controller name under correct modules so we add Api::V1::
to all users_controllers.rb
, posts_controller.rb
and comments_controller.rb
.
and we all have to move these files into the right folders.
mv app/controllers/comments_controller.rb app/controllers/posts_controller.rb app/controllers/users_controller.rb app/controllers/api/v1
Now let create some data in db/seeds.rb
to test whether it works properly.
Comment.delete_all
Post.delete_all
User.delete_all
5.times do |i|
User.create(
name: "user-#{i}",
email: "email-#{i}",
password_digest: "#{BCrypt::Password.create('password')}",
token: "#{SecureRandom.base64}"
)
end
User.all.each_with_index do |user, i|
10.times do |n|
user.posts.create(
title: "title-#{i}_#{n}",
content: "post-content-#{i}_#{n}",
)
end
end
Post.all.each_with_index do |post, i|
User.all.each_with_index do |user, i|
1.times do |n|
Comment.create(
content: "comment-content-#{i}_#{post.user.id}",
user_id: user.id,
post_id: post.id
)
end
end
end
Now if you enter the url api.blogapi.com/v1/users
, you will get all the users in database:
[{"id":71,"name":"user-0","email":"email-0","password_digest":"$2a$10$FroNrFAcoRsFG9grmy.UZO2YYUXODUotUh5.2H08zmW9jqTFrXhdK","created_at":"2017-03-25T20:36:04.386Z","updated_at":"2017-03-25T20:36:04.386Z","token":"5F8P4uvrnnjC/Iu2NkWjDg=="},{"id":72,"name":"user-1","email":"email-1","password_digest":"$2a$10$/WFbgBQgvnnwXlxTXY5WtuNT/KwXa1IlGzNHIDQNoB0ZI9dee7GAK","created_at":"2017-03-25T20:36:04.558Z","updated_at":"2017-03-25T20:36:04.558Z","token":"/iHfabZdfEmbYsbzMrGFUQ=="},{"id":73,"name":"user-2","email":"email-2","password_digest":"$2a$10$sE/foAOmicLrXkSGlXz8S.lpBAZoAQu68kfndhpdwA589hgWEz3/K","created_at":"2017-03-25T20:36:04.721Z","updated_at":"2017-03-25T20:36:04.721Z","token":"EjMzsaIzIikXh0M2Ls53CA=="},{"id":74,"name":"user-3","email":"email-3","password_digest":"$2a$10$Rgxa4jzQ6RCdtKVWo/53VuiLMk9/TCA3qb6UKn6zgyaOPfLU29lzm","created_at":"2017-03-25T20:36:04.887Z","updated_at":"2017-03-25T20:36:04.887Z","token":"A5942A9sENIhnaWmvoJXeg=="},{"id":75,"name":"user-4","email":"email-4","password_digest":"$2a$10$GzAuzBQE0PuMhvnN9AXxouSX2IrVbX55a06QXjhDEGvAPYmlR4qY.","created_at":"2017-03-25T20:36:05.054Z","updated_at":"2017-03-25T20:36:05.054Z","token":"7cXgWt20ZnA6WEWxB7jULA=="}]
Our Api works. OK, now the post is a bit long and there is still a few more step to go. So we will continue in the next post. Hope you enjoy it!
All rights reserved