Adds support for new Rails controller actions such as pagination, filtering, sorting, and selecting specific model fields. Easily implement full CRUD functionality by importing Restme into your controller.
This gem manages your controller's responsibilities for:
- Read Actions: Provide complete pagination, filtering, sorting, and field selection for records, all handled through query parameters (e.g.,
http://127.0.0.1/products?name_equal=foo
). - Create/Update Actions: Enabling automatic creation and updating of records.
gem install restme
OR
gem 'restme'
- If your controller defines an instance variable named
current_user
, Restme will automatically assign it tomodel.current_user
during create and update actions—provided your model responds to thecurrent_user
method. - Your user model must have a role attribute (user.role).
- Your controllers must be named using the plural form of the model (e.g., Product → ProductsController). Alternatively, you can manually set the model name by defining the MODEL_NAME constant (e.g., MODEL_NAME = "Shopping").
- You must create a folder inside app named restfy to define controller rules for authorization, scoping, creation, updating, and field selection (see example below).
Rails API project example here: everson-ever/restme-example#1
Include the Restme module in your controller (or, optionally, in your ApplicationController if you want it available globally):
include Restme::Restme
before_action :initialize_restme # This will authorize or deny the current action based on `ProductsController::Authorize::Rules` (see example below)
ProductsController example with restme
module Api::V1::Products
class ProductsController < ApplicationController
include Restme::Restme
before_action :initialize_restme
def index
render json: pagination_response, status: restme_scope_status
end
def show
render json: model_scope_object, status: restme_scope_status
end
def create
render json: creatable_record, status: restme_create_status
end
def update
render json: updateable_record, status: restme_update_status
end
end
end
restme_scope_status
,restme_create_status
, andrestme_update_status
are dynamic status codes generated by Restme.model_scope_object
returns a single record or nil base on scope users.pagination_response
returns the dynamic collection response generated by Restme.
Each Restme controller works together with its corresponding rules defined inside app/restfy. Restme dynamically loads the rules for each controller.
For example, ProductsController has the following rules:
- Used to verify if a user is authorized to access the current action based on their role.
module ProductsController::Authorize
class Rules
ALLOWED_ROLES_ACTIONS = {
create: %i[manager],
index: %i[client manager],
show: %i[client manager],
update: %i[manager]
}.freeze
end
end
- Used to check if the current create action is within the user's scope. The example below checks if a manager is authorized to create a product for a specific establishment_id.
CREATABLE_ACTIONS_RULES
defines which controller actions are considered POST actions.
module ProductsController::Create
class Rules
CREATABLE_ACTIONS_RULES = %i[create].freeze
# The initialization of the create rules must receive the following three parameters: current_record (the record in memory), current_user, and the controller's params.
def initialize(temp_record, current_user, controller_params = {})
@temp_record = temp_record
@current_user = current_user
@controller_params = controller_params
end
def create_manager_scope?
@current_user.manager.establishments.exists?(id: @temp_record.establishment_id)
end
end
end
- Used to check if the current update action is within the user's scope. The example below checks if a manager is authorized to update a product for a specific establishment_id.
UPDATABLE_ACTIONS_RULES
defines which controller actions are considered PUT actions.
module ProductsController::Update
class Rules
UPDATABLE_ACTIONS_RULES = %i[update].freeze
# The initialization of the create rules must receive the following three parameters: current_record (the record in memory), current_user, and the controller's params.
def initialize(temp_record, current_user, controller_params = {})
@temp_record = temp_record
@current_user = current_user
@controller_params = controller_params
end
def update_manager_scope?
@current_user.manager.establishments.select(:id).ids.include?(@temp_record.establishment_id)
end
end
end
This rule defines which records are part of the current user's scope (i.e., visible to them).
module ProductsController::Scope
class Rules
# The initialization of the scope rules must receive the following three parameters: current_record (the record in memory), current_user, and the controller's params.
def initialize(klass, current_user, controller_params = {})
@klass = klass
@current_user = current_user
@controller_params = controller_params
end
def client_scope
@klass.all
end
def deliveryman_scope
@klass.all
end
def manager_scope
@klass.where(establishment_id: @current_user.manager.establishments.ids)
end
end
end
This rule defines which nested_fields are selectable (nested fields are model relationships).
module ProductsController::Field
class Rules
NESTED_SELECTABLE_FIELDS = {
unit: {
table_name: :units
},
establishment: {
table_name: :establishments
},
category: {
table_name: :categories
}
}.freeze
end
end
To enable data filtering, specify the filterable fields in your model using the FILTERABLE_FIELDS
constant (e.g., FILTERABLE_FIELDS = %[name]
).
You can filter data using the following query parameters:
-
in: Filters by matching any of the specified values.
- Example:
id_in=1,2,3
- Example:
-
equal: Filters by exact match.
- Example:
name_equal=foo
- Example:
-
like: Filters by partial match (using
LIKE
SQL operator).- Example:
name_like=foo
- Example:
-
bigger_than: Filters by records where the field is greater than the specified value.
- Example:
created_at_bigger_than=2025-01-01
- Example:
-
less_than: Filters by records where the field is less than the specified value.
- Example:
created_at_less_than=2025-01-01
- Example:
-
bigger_than_or_equal_to: Filters by records where the field is greater than or equal to the specified value.
- Example:
created_at_bigger_than_or_equal_to=2025-01-01
- Example:
-
less_than_or_equal_to: Filters by records where the field is less than or equal to the specified value.
- Example:
created_at_less_than_or_equal_to=2025-01-01
- Example:
Example request:
http://127.0.0.1/api/v1/products?name_equal=foo,establishment_id_in=1,2,3
To enable data sorting, specify the sortable fields in your model using the SORTABLE_FIELDS
constant (e.g., SORTABLE_FIELDS = %[id]
).
You can sort records using the following query parameters:
Examples
-
Descending (DESC): Sort records in descending order.
http://localhost:3000/api/v1/products?id_sort=desc
-
Ascending (ASC): Sort records in ascending order.
http://localhost:3000/api/v1/products?id_sort=asc
Pagination functionality is available for any index
endpoint that uses the pagination_response
variable in the controller.
There are two query parameters available to control pagination:
per_page
: Defines the number of items per page.page
: Sets the current page number.
ℹ️ Note: The maximum number of items per page is currently limited to 100.
Example usage:
http://localhost:3000/api/v1/products?per_page=12&page=1
You can select specific fields from your model, such as id
, name
, or created_at
.
The resulting query will retrieve only the selected fields directly from the database, improving performance.
Example:
http://localhost:3000/api/v1/products?fields_select=id,name
You can also select specific relationships of your records, such as product.client
or product.establishment
.
The resulting query will automatically include the related data, optimizing the response for your needs.
ℹ️ Note:
If you select a nested field, you must also ensure that the related foreign key (e.g., establishment_id
, client_id
) is included via fields_select
.
If the fields_select
parameter is not provided, Restme will automatically handle the necessary field selections.
Example:
http://localhost:3000/api/v1/products?nested_fields_select=client
{
"products": [
{
"id": 1,
"name": "foo",
"client": {
"id": 5,
"name": "bar"
}
}
]
}
If your model uses Active Storage and has attachments, you can retrieve the URL of an attached file by using the attachment_fields_select
parameter.
When specified, Restme will include the URL of the attachment in the response.
ℹ️ Note:
If the specified attachment field does not exist in the model, Restme will return an HTTP 422 Unprocessable Entity error.
Example:
http://localhost:3000/api/v1/products?attachment_fields_select=image
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Restme project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.