ApiBlocks provides simple and consistent Rails API extensions.
Links:
gem'api-blocks'In an initializer such as config/initializers/api_blocks.rb you can enable the
optional blueprinter and
batch-loader integration:
ApiBlocks.configuredo |config|
config.blueprinter.use_batch_loader=trueendThis allows you to use batch-loader in order to avoid n+1 queries when
serializing associations in blueprints.
This has some caveats which are documented in association_extractor.rb.
Include ApiBlocks::Controller in your api controller:
classApi::V1::ApplicationController < ActionController::APIincludeApiBlocks::Controllerpundit_scope:api,:v1endIncluding the module will:
- Setup ApiBlocks::Responder as a responder.
- Add the
verify_request_format!before_action hook. - Setup Pundit, rescue its errors, setup its validation hooks and provide the
pundit_scopemethod.
An ActionController::Responder with better error handling and Dry::Monads::Result support.
Errors are handled for the following cases:
- The responded resource is an
ApplicationRecordsubclass and has error. - The responded resource is a
ActiveRecord::RecordInvalidexception. - Otherwise the error is re-raised to be handled through the usual Ruby On Rails error handlers.
In addition, the responder will render resources on POST and PUT rather than
returning a redirection.
It implements a basic interactor base class using dry-transaction and dry-validation under the hood.
It provides to predefined steps:
validate_input!which will validate the interactor input according to its schema.database_transaction!an around step that wraps the interactor in an ActiveRecord transaction.
Example:
classRequests::MarkAsRead < ApiBlocks::Interactorinputdoschemadorequired(:request).filled(type?: Request)endendaround:database_transaction!step:validate_input!try:update_request!,catch: ActiveRecord::RecordInvalidtry:create_history_item!,catch: ActiveRecord::RecordInvaliddefupdate_request!(request:)request.update!(read_at: Time.now.utc)requestenddefcreate_history_item!(request)request.request_history_items.create!(kind: :read)requestendendImplement an API for passwords reset using doorkeeper and devise.
Include the ApiBlocks::Doorkeeper::Passwords::Controller module in your
passwords api controller and define the user_model method to return the
concerned devise user model.
# app/controllers/api/v1/passwords_controller.rbclassApi::V1::PasswordsController < Api::V1::ApplicationControllerincludeApiBlocks::Doorkeeper::Passwords::Controllerprivatedefuser_modelUserendendThen add the approriate routes to your configuration.
# config/routes.rbRails.application.routes.drawdoscopemodule: :apidonamespace:v1doresources:passwords,only: %i[create]doget:callback,on: :collectionput:update,on: :collectionendendendendInclude the ApiBlocks::Doorkeeper::ResetPassword module so devise will forward
the doorkeeper application to the mailer.
# app/models/user.rbclassUser < ApplicationRecordincludeApiBlocks::Doorkeeper::ResetPasswordendInclude the reset password Doorkeeper::Application extensions.
# config/initializers/doorkeeper.rbDoorkeeper.configuredo# ...endclass ::Doorkeeper::Application < ActiveRecord::BaseincludeApiBlocks::Doorkeeper::Passwords::ApplicationendOverride your devise mailer #reset_password_instructions method to add the
application parameter.
# app/mailers/devise_mailer.rbclassDeviseMailer < Devise::Mailerdefreset_password_instructions(record,token,application=nil,_opts={})@token=token@application=applicationendendUpdate the devise mailer template to link to the callback API.
# app/views/devise/mailer/reset_password_instructions.html.erb
<p><%=link_to"Change my password",callback_v1_passwords_url(reset_password_token: @token)%></p>Finally, generate the required migrations:
bundle exec rails g api_blocks:doorkeeper:passwords:migrationImplement an API for devise_invitable using doorkeeper.
Include the ApiBlocks::Doorkeeper::Invitations::Controller module in your api
controller and define the user_model method to return the concerned devise
user model.
# app/controllers/api/v1/invitations_controller.rbclassApi::V1::InvitationsController < Api::V1::ApplicationControllerincludeApiBlocks::Doorkeeper::Invitations::Controllerprivatedefuser_modelUserendendAdd the approriate routes to your configuration.
# config/routes.rbRails.application.routes.drawdoscopemodule: :apidonamespace:v1doresources:invitations,only: %i[createshow]doget:callback,on: :collectionput:update,on: :collectionendendendendInclude the invitations Doorkeeper::Application extensions.
# config/initializers/doorkeeper.rbDoorkeeper.configuredo# ...endclass ::Doorkeeper::Application < ActiveRecord::BaseincludeApiBlocks::Doorkeeper::Invitations::ApplicationendOverride your devise mailer #invitation_instructions method to add the
application parameter.
# app/mailers/devise_mailer.rbclassDeviseMailer < Devise::Mailerdefinvitation_instructions(_record,token,application: nil, **_opts)@token=token@application=applicationsuperendendUpdate the devise mailer template to link to the callback API.
# app/views/devise/mailer/invitation_instructions.html.erb
<p><%=link_tot("devise.mailer.invitation_instructions.accept"),callback_v1_invitations_url(invitation_token: @token,client_id: @application.uid)%></p>Finally, generate the required migrations:
bundle exec rails g api_blocks:doorkeeper:invitations:migrationLicensed under the MIT license, see the separate LICENSE.txt file.