SHFT Rails API Boilerplate

SHFT
4 min readApr 7, 2023

SHFT Rails boilerplate ile daha yönetilebilir bir yapı oluşturarak zaman kazanın.

SHFT Rails API Boilerplate
SHFT x Ruby on Rails

SHFT Rails API Boilerplate Nedir? Ne İşe Yarar?

Ruby on Rails’in kendi içinde getirdiği yapının büyük uygulamalarda ortaya çıkardığı karmaşıklığın önüne geçmek amacıyla birbirinden ayrı ama birbiri ile veri akışı sağlayan özel uygulama katmanları inşa ettik. Bu katmanlar sayesinde test edilebilirlik ve yönetilebilirlik açısından yeni bir boyut kazandırmış olduk.

Öncelikle Rails API Boilerplate, Rails ile geliştirebileceğimiz API uygulamaları için kendi içinde Authentication, Service Layer, Contract Layer, Operation Layer, Open-API, Searching ve Pagination gibi çözümleri içinde getiren bir yapıdır. Bu yapının elemanlarından kısaca bahsedelim.

Service Layer: Uygulamamızdaki tüm iş mantıklarının (business-logic) yer aldığı katmandır. Bu katmanın amacı, uygulamamızın iş mantığını bulundurmak ve yürütmektir. Bu katmandaki sınıflar sadece Success ve Failure döndürürler. Success döndürdükleri zaman işlem başarılıdır, Failure döndürdükleri zaman ise işlem başarısızdır. Bu sayede hata fırlatmadan işlem sonucunu döndürürler.

# app/services/users/registrations/register_service.rb

module Users
module Registrations
class RegisterService < ApplicationService
include Supports::Doorkeeper::CustomRegisterResponse

option :params, type: Types::Hash
option :doorkeeper_application, type: Types.Instance(Doorkeeper::Application)

def call
ActiveRecord::Base.transaction(requires_new: true) do
user = yield create_user
access_token = yield create_access_token(user)
response = body(user, access_token)

Success(response)
end
end

private

def create_user
Users::CreateService.new(params:).call
end

def create_access_token(user)
Doorkeeper::AccessTokens::CreateService.new(user:, doorkeeper_application:).call
end
end
end
end

Contract Layer: Client tarafından gönderilen tüm parametrelerin validasyonunu yapar. Bu sayede uygulamamıza gelen parametrelerin doğruluğunu kontrol ederiz ve uygulama içinde type-safe bir şekilde parametreleri kullanabiliriz.

# app/contracts/users/registrations/register_contract.rb

module Users
module Registrations
class RegisterContract < ApplicationContract
params do
required(:email).filled(Types::Email)
required(:password).filled(:str?, min_size?: Devise.password_length.min)
end
end
end
end

Operation Layer: Bu katmanın amacı, Contract Layer’dan Success döndüğü zaman parametreleri Service Layer’a göndermek ve Service Layer’dan dönen sonucu client tarafına iletmektedir. Bu sayede uygulamamızın iş mantığını ve iş kurallarını tutan Service Layer’ı diğer katmanlardan soyutlamış oluruz.

# app/operations/users/registrations/create_operation.rb

module Users
module Registrations
class CreateOperation < ApplicationOperation
option :params
option :doorkeeper_application, type: Types.Instance(Doorkeeper::Application)
option :contract, default: proc { Users::Registrations::RegisterContract.new }

def call
contract_params = yield validate(contract)
user = yield call_service(contract_params)

Success(user)
end

private

def call_service(contract_params)
Users::Registrations::RegisterService.new(params: contract_params, doorkeeper_application:).call
end
end
end
end

Yukarıda bahsettiğimiz katmanların çalışma mantığını daha iyi anlamak için aşağıdaki diagramı inceleyebilirsiniz.

SHFT Rails API Boilerplate Diagram

Authentication: Uygulamamızın Authentication işlemlerini yönetmek için Devise ve Doorkeeper gemleri kullanılmıştır. Devise ile uygulamamızın Authentication işlemlerini yönetirken, Doorkeeper ile ise uygulamamızın Token tabanlı Authentication işlemlerini yönetiriz. Bu sayede Devise ile yönettiğimiz Authentication işlemlerini API üzerinden de kullanabiliriz.

Open-API: Uygulamamızın API dokümantasyonunu oluşturmak için Swagger Blocks gemi kullanılmıştır. Bu gem ile uygulamamızın API dokümantasyonunu oluşturup Swagger UI üzerinden inceleyebiliriz.

# app/swagger_docs/controllers/v1/users/registrations_controller.rb

module Controllers
module V1
module Users
class RegistrationsController
include Swagger::Blocks

swagger_path '/v1/users/sign_up' do
operation :post do
key :summary, 'Sign up'
key :description, 'Create a new user and generate access and refresh tokens'
key :operationId, 'userSignUp'
key :tags, [
'User Registrations'
]

request_body do
key :description, 'User credentials'
key :required, true
content :'application/json' do
schema do
key :'$ref', :UserSignUpInput
end
end
end

response 201 do
key :description, 'Successful response'
content :'application/json' do
schema do
key :'$ref', :UserSignUpSuccessResponse
end
end
end

response 422 do
key :description, 'Something goes wrong'
content :'application/json' do
schema do
key :'$ref', :ErrorResponse
end
end
end

response 401 do
key :description, 'Invalid client credentials passed'
content :'application/json' do
schema do
key :'$ref', :ErrorResponse
end
end
end
end
end
end
end
end
end

Searching: Uygulamamızın API üzerinden arama işlemlerini yönetmek için Ransack gemi kullanılmıştır. Ransack için gerekli olan parametrelerin doğruluğunu kontrol etmek için QueryObject sınıfını kullanıyoruz. Bu sayede uygulamamızın API üzerinden arama işlemlerini yönetirken, parametrelerin doğruluğunu kontrol edebiliriz. bu sınıfa erişmek için Controller’da query_object metodunu kullanabiliriz.

# app/controllers/projects/invitations_controller.rb

module Projects
class InvitationsController < AuthenticatedController
def index
service = ProjectsService::Invitations::List.new(project: current_project,
query_object: query_object).call

@invitations = service.success
end
end
end

Pagination: Uygulamamızın API üzerinden sayfalama (pagination) işlemlerini yönetmek için Kaminari gemi kullanılmıştır. Kaminari için gerekli olan parametrelerin doğruluğunu kontrol etmek için PaginationObject sınıfını kullanıyoruz. Bu sayede uygulamamızın API üzerinden sayfalama işlemlerini yönetirken, parametrelerin doğruluğunu kontrol edebiliriz. Bu sınıfa erişmek için Controller’da pagination_object metodunu kullanabilirsiniz.

# app/controllers/projects/invitations_controller.rb

module Projects
class InvitationsController < AuthenticatedController
def index
service = ProjectsService::Invitations::List.new(project: current_project,
pagination: pagination_object).call

@invitations = service.success
end
end
end

Sonuç

SHFT Rails API Boilerplate ile ilgili temel konulardan, kullanımlardan ve örneklerden bahsetmeye çalıştık. Şu an Github’da 50’den fazla star almış open-source bir projedir. Uygulamamızı kullanmak yada katkıda bulunmak isterseniz buraya tıklayabilirsiniz.

Gelecek yazılarda görüşmek dileğiyle, teşekkürler 🙂

Nejdet Kadir Bektaş | Backend Team Lead

--

--