
Security News
Browserslist-rs Gets Major Refactor, Cutting Binary Size by Over 1MB
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
Rails REST API's made easy.
Developing REST API's in a conventional manner involves many challenges. Between the parameters, route details, payloads, responses, and API documentation, it's difficult to find an implementation strategy that minimizes repetition and organizes the details in a simple, easy-to-maintain manner. This is where model-api comes in.
With model-api, you can:
This README file is intended to provide a brief introduction. For more detailed information, the following guides are available:
General information on the API (including enumerated constants, etc.) can be found here:
Put this in your Gemfile:
gem 'model-api'
The model-api gem doesn't require configuration. However, if you plan to generate OpenAPI
documentation, add an open_api.rb
file to config/initializers
and configure as follows:
OpenApi.configure do |config|
# Default base path(s), used to scan Rails routes for API endpoints.
config.base_paths = ['/widget-api/v1']
# General information about your API.
config.info = {
title: 'Acme Widget API',
description: "Documentation of the Acme's Widget API service",
version: '1.0.0',
terms_of_service: 'https://www.acme.com/widget-api/terms_of_service',
contact: {
name: 'Acme Corporation API Team',
url: 'http://www.acme.com/widget-api',
email: 'widget-api-support@acme.com'
},
license: {
name: 'Apache 2.0',
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
}
}
# Default output file path for your generated Open API JSON document.
config.output_file_path = Rails.root.join('apidoc', 'api-docs.json')
end
To expose a resource, start by defining the underlying model for that resource:
class Book < ActiveRecord::Base
include ModelApi::Model
# Valication rules are utilized by model-api to validate requests
validates :name, presence: true, uniqueness: true, length: { maximum: 50 }
validates :description, length: { maximum: 250 }
validates :isbn, presence: true, uniqueness: true, length: { maximum: 13 }
# Define model-level rules and metadata associated with the REST API.
api_model(
base_query: ->(opts) { opts[:admin] ? Book.where(public: true) : Book.all }
)
# Define the attributes exposed via the REST API.
api_attributes(
id: { filter: true, sort: true },
name: { filter: true, sort: true },
description: {},
isbn: { filter: true, sort: true,
parse: ->(v) { v.to_s.gsub(%r{[^\d]+}, '') },
render: ->(v) { "#{v[0..2]}-#{v[3]}-#{v[4..5]}-#{v[6..11]}-#{v[12..-1]}" }
},
created_at: { read_only: true, filter: true },
updated_at: { read_only: true, filter: true }
)
end
An explanation of the options used in the api_model
example above:
base_query
- Limit data exposed by the API based on context (e.g. current user).An explanation of the options used in the api_attributes
example above:
filter
- Allow filtering by query string parameters (e.g. ?id=123
).sort
- Allow use of this column in the sort_by parameter.read_only
- Disallow column updates (via POST
, PUT
, or PATCH
).parse
- Method name (e.g. :to_i
) or proc / lambda for pre-processing incoming payload values.
(The example lambda removes any non-digit values (such as dashes) from supplied ISBN values.)render
- Method name or proc / lambda that formats values returned in response payloads.
(The example lambda formats the 13-digit ISBN number using the typical dash convention.)Next, define the base controller class that all of your API controllers will extend
(for this example, in app/controllers/api/v1/base_controller.rb
):
module Api
module V1
class BaseController < ActionController::Base
include ModelApi::BaseController
include ModelApi::OpenApiExtensions
include OpenApi::Controller
# OpenAPI documentation metadata shared by all endpoints, including common query string
# parameters, HTTP headers, and HTTP response codes.
open_api_controller \
query_string: {
access_token: {
type: :string,
description: 'OAuth 2 access token query parameter',
required: false
}
},
headers: {
'Authorization' => {
type: :string,
description: 'Authorization header (format: "bearer <access token>")',
required: false
}
},
responses: {
200 => { description: 'Successful' },
400 => { description: 'Not found' },
401 => { description: 'Invalid request' },
403 => { description: 'Not authorized (typically missing / invalid access token)' }
}
# OpenAPI documentation for common API endpoint path parameters
open_api_path_param :book_id, description: 'Book identifier'
# HATEOAS links common to all responses (e.g. a common terms-of-service link)
def common_response_links(_opts = {})
{ 'terms-of-service' => URI(url_for(controller: '/home', action: :terms_of_service)) }
end
end
end
end
Add the resource routes to your routes.rb
file:
namespace :api do
namespace :v1 do
resource :books, except: [:new, :edit], param: :book_id
end
end
Finally, add a controller for your new resource (for this example, in
app/controllers/api/v1/base_controller.rb
):
module Api
module V1
class BooksController < BaseController
class << self
# Default model class to use for API endpoints in this controller
def model_class
Book
end
# Default options for model-api helper methods used to process requests to endpoints
def base_api_options
super.merge(id_param: :book_id)
end
end
# OpenAPI metadata describing the collective set of endpoints defined in this controller
open_api_controller \
tag: {
name: 'Books',
description: 'Comprehensive list of available books'
}
# GET /api/v1/books endpoint OpenAPI doc metadata and implementation
add_open_api_action :index, :index, base_api_options.merge(
description: 'Retrieve list of available books')
def index
render_collection collection_query, base_api_options
end
# GET /api/v1/books/:book_id endpoint OpenAPI doc metadata and implementation
add_open_api_action :show, :show, base_api_options.merge(
description: 'Retrieve details for a specific book')
def show
render_object object_query.first, base_api_options
end
# POST /api/v1/books endpoint OpenAPI doc metadata and implementation
add_open_api_action :create, :create, base_api_options.merge(
description: 'Create a new book')
def create
do_create base_api_options
end
# PATCH/PUT api/v1/books/:book_id endpoint OpenAPI doc metadata and implementation
add_open_api_action :update, :update, base_api_options.merge(
description: 'Update an existing book')
def update
do_update object_query, base_api_options
end
# DELETE /api/v1/books/:book_id endpoint OpenAPI doc metadata and implementation
add_open_api_action :destroy, :destroy, base_api_options.merge(
description: 'Delete an existing book')
def destroy
do_destroy object_query, base_api_options
end
def object_query(opts = {})
super(opts.merge(not_found_error: true))
end
end
end
end
To generate OpenAPI documentation:
rake open_api:docs
Optionally, you may specify a base route path and output file:
rake open_api:docs[/api/v1,/home/myhome/api-v1.json]
FAQs
Unknown package
We found that model-api demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
Research
Security News
Eight new malicious Firefox extensions impersonate games, steal OAuth tokens, hijack sessions, and exploit browser permissions to spy on users.
Security News
The official Go SDK for the Model Context Protocol is in development, with a stable, production-ready release expected by August 2025.