12/08/2018, 13:18

xây dựng API với Napa

Hiện nay Việc xây dựng API dường như đã quá quen thuộc với mỗi lập trình viên. Bằng ngôn ngữ Ruby chúng ta có rất nhiều cách để viết API. Và để hỗ trợ viết API nhanh và tiện nhất thì hiện nay có một framework giúp ta thực hiện điều đó. Đó là Napa. Cài đặt gem install napa chú ý là napa chỉ ...

Hiện nay Việc xây dựng API dường như đã quá quen thuộc với mỗi lập trình viên. Bằng ngôn ngữ Ruby chúng ta có rất nhiều cách để viết API. Và để hỗ trợ viết API nhanh và tiện nhất thì hiện nay có một framework giúp ta thực hiện điều đó. Đó là Napa.

Cài đặt

gem install napa

chú ý là napa chỉ hỗ trợ cho Ruby từ 2.0 trở lên.

Khởi tạo project

napa new demo-napa

CSDL mặc định của napa là mysql. Cấu trúc thư mục của project như sau

demo-napa
- app
  - apis
  - models
  - representers
- config
- db
- lib
- log
- spec
- Gemfile
- Rakefile

Khá giống với rails nhưng được giản lược hơn. Hầu hết code của chúng ta sẽ viết trong thư mục app. Ta thấy có 1 thư mục khá mới là representers sẽ chưá các file làm nhiệm vụ convert model thành cấu trúc JSON.

Ta tiến hành bundle install như với rails

cd demo-napa
bundle install

Tiếp đó ta config database trong file .env và

rake db:create

Nếu có lỗi không tạo đượ database do sai password thì do phiên bản napa không tương thích với mysql. Để khắc phục ta vào Gemfile và sửa

gem 'napa'
thành
gem 'napa', github: "bellycard/napa"

rồi tiến hành

bundle update

Viết API

trước tiên ta cũng tạo model như với rails. Ở đây ta tạo model Contact với các thuộc tính cơ bản

napa generate model Contact name:string email:string phone:string

sau đó chạy migrate

rake db:migrate

Tạo API end point

napa generate api contact

napa sẽ generate API class app/apis/contacts_api.rb và representer class app/representers/contact_representer.rb. contacts_api.rb sẽ có cú pháp và cấu trúc giống với Grape API. Ta sẽ viết các hàm restful cho API contact

class ContactsApi < Grape::API
  desc 'Get a list of contacts'
  params do
    optional :ids, type: Array, desc: 'Array of contact ids'
  end
  get do
    contacts = params[:ids] ? Contact.where(id: params[:ids]) : Contact.all
    represent contacts, with: ContactRepresenter
  end

  desc 'Create a contact'
  params do
  end
  post do
    contact = Contact.create!(permitted_params)
    represent contact, with: ContactRepresenter
  end

  params do
    requires :id, desc: 'ID of the contact'
  end
  route_param :id do
    desc 'Get an contact'
    get do
      contact = Contact.find(params[:id])
      represent contact, with: ContactRepresenter
    end

    desc 'Update a contact'
    params do
    end
    put do
      # fetch contact record and update attributes.  exceptions caught in app.rb
      contact = Contact.find(params[:id])
      contact.update_attributes!(permitted_params)
      represent contact, with: ContactRepresenter
    end
  end
end

Mặc định napa sẽ để trống phần khai báo các params. Tuỳ vào login của chúng ta mà sẽ khai báo thêm. Ví dụ các params bắt buộc thì ta dùng require, hay nếu ko bắt buộc thì ta dùng optional

...
desc 'Create an contact'
params do
  optional :name, type: String, desc: 'The Name of the contact'
  optional :phone, type: String, desc: 'The Phone of the contact'
  optional :email, type: String, desc: 'The Email Address of the contact'
end
...

...
desc 'Update a contact'
params do
  optional :name, type: String, desc: 'The Name of the contact'
  optional :phone, type: String, desc: 'The Phone of the contact'
  optional :email, type: String, desc: 'The Email Address of the contact'
end
...

Tiếp theo ta sẽ update file presenter. Ta sẽ khai báo các trường của model contact mà muốn thêm vào trong JSON response.

class ContactRepresenter < Napa::Representer
  property :id, type: String
  property :name
  property :phone
  property :email
end

Cuối cùng ta khai bào trong application_api.rbđể mount API contact.

class ApplicationApi < Grape::API
  format :json
  extend Napa::GrapeExtenders

  mount ContactsApi => '/contacts'

  add_swagger_documentation
end

Thực hiện request

Ta đã thực hiện xong việc viết API. Bây giờ ta bật server để thực hiện request

shotgun

Để test việc thực hiện request có rất nhiều cách như dùng postman, rest client hay thực hiện curl trên terminal.

Đầu tiên ta thực hiện tạo contact

curl -X POST -d name="Devdatta Kane" -d email="kane.devdatta@gmail.com" -d phone="25451512544" http://localhost:9393/contacts

Ta sẽ nhận đuuwocj response

{
  "data": {
    "object_type": "contact",
    "id": "1",
    "name": "Devdatta Kane",
    "email": "kane.devdatta@gmail.com",
    "phone": "25451512544"
  }
}

Bây giờ ta sẽ thực hiện request để get contact vừa tạo được

curl -X GET http://localhost:9393/contacts

Response trả về

{
  "data": [
    {
      "object_type": "contact",
      "id": "1",
      "name": "Devdatta Kane",
      "email": "kane.devdatta@gmail.com",
      "phone": "25451512544"
    }
  ]
}

Tương tự ta có thể update contact bằng việc thực hiện request đến API.

Bảo mật

Bên trên ta đã thực hiên viết API và đã request thành công tuy nhiên ta cần thiết lâoj chế độ bảo mật cho API nếu ko muốn bất cứ ai cũng có thể request đến API và xem, sửa, xoá dữ liệu. Để làm việc này thì ta sẽ yêu cầ mỗi request đến API đều phải có access_token hợp lệ, nếu không sẽ báo lỗi.

Token này có thể lấy từ user. mỗi user sẽ dược sinh ra 1 token và ta cần truyền đúng token mói request đc API.

Ta viết thêm hàm check trong

  before do
    error!("401 Unauthorized", 401) unless authenticated?
  end

  helpers do
    def authenticated?
      # return true if valid token
    end
  end

Bây giờ thử request mà không truyền access_ken:

curl -X GET http://localhost:9393/contacts

thì ta sẽ nhận được response

{"error":{"code":"api_error","message":"401 Unauthorized"}}

Kết luận

Có thể nói napa là phương tiện viết API bằng ngôn ngữ Ruby khá mạnh. Chúng ta có thể nhanh chóng triển khai API bằng napa nếu project của ta không quan tâm đến view. Trên đây chỉ mang tính giới thiệu cơ bản về napa. Khi triển khai trong dự án thật ta cần đầu tư thêm về cấu trúc API và quản lý version ...

Tham khảo

https://github.com/bellycard/napa

0