Xây dựng API với Grape
Hệ thống bạn đang xây dựng cần viết API, bạn đang băn khoăn không biết xây dựng API như thế nào cho dễ quản lí, mang lại hiệu quả cao. Bài viết này mình xin giới thiệu Grape, 1 gem hữu hiệu để quản lí API với nhiều ưu điểm nổi bật Theo Grape thì grape là Grape is a REST-like API framework ...
Hệ thống bạn đang xây dựng cần viết API, bạn đang băn khoăn không biết xây dựng API như thế nào cho dễ quản lí, mang lại hiệu quả cao. Bài viết này mình xin giới thiệu Grape, 1 gem hữu hiệu để quản lí API với nhiều ưu điểm nổi bật
Theo Grape thì grape là
Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more. Grape là 1 REST-like API cho Ruby,. Nó được thiết kế để chạy trên Rack hoặc bổ sung cho mô hình ứng dụng web hiện có như Rails và Sinatra bằng việc cung cấp 1 DSL đơn giản để dễ dàng phát triển các RESTful API
Ưu điểm của Grape:
- Khả năng phát triển nhanh, có thể sử dụng cùng Ruby on Rails hoặc sử dụng độc lập
- Linh hoạt trong quản lí version
- Có tốc độ sử lý cao hơn so với Rails-api
Để cài đặt ta thêm cài đặt bằng cách
gem install grape
Hoặc thêm vào Gemfile:
gem 'grape'
Rồi sau đó bundle install Như vậy là đã cài gem grape thành công. Giờ chúng ta xây dựng cấu trúc file API. Ở trong controller ta tạo 1 folder api chuyên quản lí api của hệ thống
app |––controllers |––api
Grape API là ứng dụng Rack được tạo bằng các subclass kế thừa từ Grape::API
Ta tạo 1 file base trong api
app |––controllers |––api |––base.rb
Sau đó ta xây dựng base: dùng để khai báo mount trong đó. Mount có nghĩa các chức năng, code đó đã được tồn tại trong ứng dụng Rails
module API class Base < Grape::API mount API::V1::Base end end
mount :
mount là từ khóa nói vs Rails app có 1 ứng dụng khác (thường là ứng dụng Rack và chính là Grape API) tồn tại.
Versioning
Grape framework cung cấp version. Nghĩa là chúng ta sẽ tổ chức API bên trong 1 phiên bản hoặc module v1. Các phiên bản trong tương lai chúng ta có thể phát triển khi ứng dụng phát triển sẽ được nested bên trong version 2, 3...
Tạo các module
Tao folder con v1 bên trong api
app |––controllers |––api |––base.rb |––v1
Với mỗi version ta cũng xây dựng 1 base riêng cho nó:
app |––controllers |––api |––base.rb |––v1 |––base.rb
base này sẽ có trách nhiệm khai báo mount của các class khác
module API module V1 class Base < Grape::API mount V1::Users # mount API::V1::AnotherResource end end end
Chúng ta vừa mount cho class User. Giờ chúng ta hãy xây dựng nó Tạo users.rb trong v1:
app |––controllers |––api |––base.rb |––v1 |––base.rb |––users.rb
Định nghĩa Endpoints
Giả sử ta muốn xây dựng 1 api method: GET để lấy toàn bộ danh sách User Trong app/controllers/api/v1/users.rb:
module API module V1 class Users < Grape::API include API::V1::Defaults resource :users do desc "Return all users" get "", root: :users do User.all end desc "Return a user" params do requires :id, type: String, desc: "ID of the user" end get ":id", root: "user" do User.where(id: params[:user_id]).first end end end end end
class Users bên trong function API giống như 1 controller Đầu tiên chúng ta định nghĩa Routes : resource :users. Sau đó lựa chọn phương thức (GET, POST..) sao cho phù hợp. Ở đây mình lấy danh sách toàn bộ users trong hệ thống và lấy thông tin của 1 user bất kì nên mình dùng method GET Sau đó ta dùng Postman theo địa chỉ http://localhost:3000/api/v1/users sẽ ra danh sách toàn bộ users.
Before và After:
before và after callbacks được gọi theo thứ tự sau:
- before
- before_validation
- validations
- after_validation
- the API call
- after
Ví dụ:
module API module V1 class Users < Grape::API include API::V1::Defaults before do authenticate_user! end # ..... end end
Helper
Có nhiều hàm sẽ dùng chung cho các api nên ta sẽ gom chúng vào 1 file gọi là defaults.rb trong app/controllers/api/v1/defaults.rb:
module API module V1 module Defaults extend ActiveSupport::Concern included do prefix "api" version "v1", using: :path default_format :json format :json formatter :json, Grape::Formatter::ActiveModelSerializers helpers do def permitted_params @permitted_params ||= declared(params, include_missing: false) end def logger Rails.logger end end # check authentice_user def authenticate_user! uid = request.headers["Uid"] token = request.headers["Access-Token"] @current_user = User.find_by(uid: uid) unless @current_user && @current_user.valid_token?(token) api_error!("You need to log in to use the app.", "failure", 401, {}) end end # Hàm hiển thị errors message khi lỗi def api_error!(message, error_code, status, header) error!({message: message, code: error_code}, status, header) end # # Hàm raise errors message khi lỗi def api_error_log(message) @logger ||= Logger.new(ProjectLogger.log_path("project_api")) @logger.info("=============#{Time.zone.now.to_s}================== ") @logger.info("#{message} ") end rescue_from ActiveRecord::RecordNotFound do |e| error_response(message: e.message, status: 404) end rescue_from ActiveRecord::RecordInvalid do |e| error_response(message: e.message, status: 422) end rescue_from Grape::Exceptions::ValidationErrors do |e| error_response(message: e.message, status: 400) end end end end end
Routes
Trong config/routes.rb ta thêm
Rails.application.routes.draw do mount API::Base, at: "/" end
Rspec
Để viết rspec cho api ta có thể dùng gem airborne. Tham khảo tại Airborne Cài đặt:
gem install airborne
Ta thêm thư mục giống như sau:
spec |––api |––v1 |––users_spec
Ví dụ 1 đoạn rspec cho 1 api cập nhập user
require "rails_helper" require "airborne" describe "API::V1::Users" do after(:all){I18n.locale = :ja} describe "POST api/v1/users" do let!(:user) do FactoryGirl.create :user, id: 1, email: "test@gmail.com", first_name: "James", last_name: "Bond", provider: "email" end context "when user update successfully" do let(:api_response){FactoryGirl.build(:api_update_user_success_response).deep_symbolize_keys} before do post("/api/v1/users", {first_name: "hitorri"}, {"Accept-Language": "en", "App-Version": "knt/1.0", "Uid": user.uid, "Access-Token": user.access_token}) end it{expect_json(api_response)} end end end
Grape API là 1 trong số công cụ đơn giản để viết API, nó mang lại hiệu quả cao nhờ cách thức viết đơn giản và hiệu suất cao. Sự kết hợp hoàn hảo giữa gem Grape và Airborne sẽ giúp bạn quản lí api một cách tốt nhất Hi vọng bài viết có thể giúp ích cho bạn khi xây dựng API!
Tham khảo: https://github.com/ruby-grape/grape https://github.com/ruby-grape/grape http://www.thegreatcodeadventure.com/making-a-rails-api-with-grap/