apipie-rails

repository·master·Indexed 25 days ago

https://github.com/apipie/apipie-rails

A DSL and Rails engine for documenting RESTful APIs directly within Ruby code. It allows developers to describe resources, methods, parameters, and responses using a Ruby-based DSL, facilitating the auto-generation of Swagger definitions and providing built-in parameter validation and RSpec response validation.

Tokens
18.1K
Snippets
45
Records
87
Agent score
81%

What's inside apipie-rails

  1. Implement self-describing classes with describe_own_properties

    master

    If your JSON response logic is encapsulated in a class, you can make that class 'self-describing' by implementing a self.describe_own_properties class method. This method must return an array of Apipie::prop objects. When you use the class name in a returns statement, Apipie will automatically call this method to build the documentation.

    This is particularly useful for reflection-based JSON generators.

    class Pet
      def self.describe_own_properties
        [
            Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
            Apipie::prop(:animal_type, 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
            Apipie::additional_properties(false)
        ]
      end
    end
    
    class PetsController
        api :GET, "/index", "Get all pets"
        returns :array_of => Pet
        def index
         # ...
        end
    end
  2. Use Apipie Concerns for shared documentation

    master

    If your API actions are defined in a module instead of directly in the controller, you can load the Apipie DSL into that module by extending it with Apipie::DSL::Concern.

    To allow a module to be reused across different controllers with different values, use apipie_concern_subst(:key => "value") within the controller before including the module. This performs substitutions in paths, descriptions, and parameter names.

    Default Substitutions:

    • :controller_path: The value of controller.controller_path (e.g., api/users). Note: This is only used if you are not using the api! keyword.
    • :resource_id: The Apipie identifier of the resource (e.g., users). This can be set via the resource_id method.
    # users_module.rb
    module UsersModule
      extend Apipie::DSL::Concern
    
      api :GET, '/:controller_path', 'List :resource_id'
      def index
        # ...
      end
    
      api! 'Show a :resource'
      def show
        # ...
      end
    
      api :POST, '/:resource_id', "Create a :resource"
      param :concern, Hash, :required => true
        param :name, String, 'Name of a :resource'
        param :resource_type, ['standard','vip']
      end
      def create
        # ...
      end
    
      api :GET, '/:resource_id/:custom_subst'
      def custom
        # ...
      end
    end
    
    # users_controller.rb
    class UsersController < ApplicationController
    
      resource_description { resource_id 'customers' }
    
      apipie_concern_subst(:custom_subst => 'custom', :resource => 'customer')
      include UsersModule
    end
  3. Reuse parameters with `def_param_group` and `param_group`

    master

    To avoid repeating parameter definitions across multiple actions (like create and update), use parameter groups.

    1. Define a group using def_param_group :name do ... end in a controller.
    2. Apply it to an action using param_group :name.
    3. To reference a group from a different controller, use param_group :name, OtherController.

    Example:

    # In a controller
    def_param_group :address do
      param :street, String
      param :zip, String
    end
    
    api :POST, "/users", "Create"
    param_group :address
    def create
      # ...
    end
    # v1/users_controller.rb
    def_param_group :address do
      param :street, String
      param :number, Integer
      param :zip, String
    end
    
    def_param_group :user do
      param :user, Hash do
        param :name, String, "Name of the user"
        param_group :address
      end
    end
    
    api :POST, "/users", "Create a user"
    param_group :user
    def create
      # ...
    end
    
    # v2/users_controller.rb
    api :POST, "/users", "Create a user"
    param_group :user, V1::UsersController
    def create
      # ...
    end
  4. Implement API versioning

    master

    Apipie supports versioning at the resource and method levels using the api_version DSL keyword.

    • Resource level: Use api_versions "1", "2" inside a resource_description block to assign multiple versions to a resource.
    • Method level: Use api_version "1" before a specific method to override the resource's versioning.
    • Inheritance: Version settings on a parent controller are inherited by children.
    • Deprecation: Use :deprecated => true in the api definition to flag routes.

    Apipie generates versioned paths like /apipie/1/users/index. If no version is specified, it uses Apipie.configuration.default_version (defaults to "1.0").

    When querying or referencing resources, use the format "version#resource#method".

    resource_description do
      api_versions "1", "2"
    end
    
    api :GET, "/api/users/", "List: users"
    
    api_version "1"
    def index
      # ...
    end
    
    api :GET, "/api/users/", "List: users", :deprecated => true
  5. Use the property keyword for response fields

    master

    The property keyword is used to document fields that exist only in the response. It differs from param in the following ways:

    • It is :only_in => :response by default.
    • It is :required => :true by default.
    • It can represent an :array_of objects.

    Example of a nested property array:

    property :example, :array_of => Hash do
      property :number1, Integer
      property :number2, Integer
    end
  6. Use action-aware parameter groups

    master

    In CRUD operations, parameters often have different requirements depending on the action (e.g., required: true for create, but required: false for update).

    By setting :action_aware => true in a param definition within a def_param_group, Apipie automatically adjusts the required status based on the HTTP method:

    • POST/Create: required becomes true.
    • PUT/Update: required becomes false.

    You can also explicitly force an evaluation mode using the :as option (e.g., :as => :create).

    Example:

    def_param_group :user do
      param :user, Hash, :action_aware => true do
        param :name, String, :required => true
      end
    end
    
    api :POST, "/users", "Create"
    param_group :user
    def create
      # ...
    end
    
    api :PUT, "/users/:id", "Update"
    param_group :user
    def update
      # ...
    end
    def_param_group :user do
      param :user, Hash, :action_aware => true do
        param :name, String, :required => true
      end
    end
    
    api :POST, "/users", "Create a user"
    param_group :user
    def create
      # ...
    end
    
    api :PUT, "/users/admin", "Create an admin"
    param_group :user, :as => :create
    def create_admin
      # ...
    end
    
    api :PUT, "/users/:id", "Update a user"
    param_group :user
    def update
      # ...
    end
  7. Reuse param_groups for both requests and responses

    master

    You can define a single :param_group that handles both input (request) and output (response) by using the :only_in option. This prevents duplication in CRUD operations.

    • Use param :field, Type, :only_in => :request for fields sent by the client but not returned by the server.
    • Use property :field, Type (or param :field, Type, :only_in => :response) for fields returned by the server but not accepted in the request.
    • Use param :field, Type without :only_in for fields common to both.
  8. Use `:only_in` to differentiate request and response parameters

    master

    When reusing a param_group for both inputs and outputs, use the :only_in option to specify whether a parameter belongs to the request or the response. This prevents fields like :id (which is in the response but not the request body) from causing validation errors.

    Options:

    • :only_in => :response: Parameter is included in the response but ignored in requests.
    • :only_in => :request: Parameter is included in the request but ignored in the response.
    def_param_group :user do
      param :user, Hash, :desc => "User info", :required => true, :action_aware => true do
        param :id, Integer, :only_in => :response
        param :requested_id, Integer, :only_in => :request
        param_group :credentials
        param :membership, ["standard","premium"], :desc => "User membership", :allow_nil => false
      end
    end
    
    api :GET, "/users/:id", "Get user record"
    returns :user, :desc => "the requested record"
    error :code => 404, :desc => "no user with the specified id"
  9. Process request parameters with custom logic

    master

    You can transform incoming request parameters before validation. For example, converting a comma-separated string into an array or handling Rails' default behavior of converting empty arrays to nil.

    To enable this, set process_params to true in your configuration. You can also use the as option to map API parameter names to different internal names used in your code.

    To implement transformation logic, define a process_value method within your validator.

    def process_value(value)
     value ? value.split(',') : []
    end
  10. Describe multiple return codes

    master

    To document different possible HTTP status codes for a single API endpoint, repeat the returns keyword for each specific case. Each returns block can define a unique response structure or param-group.

    api :GET, "/pets/:id/extra_info", "Get extra information about a pet"
      returns :desc => "Found a pet" do
        param_group :pet
        property 'pet_history', Hash do
          param_group :pet_history
        end
      end
      returns :code => :unprocessable_entity, :desc => "Fleas were discovered on the pet" do
        param_group :pet
        property :num_fleas, Integer, :desc => "Number of fleas on this pet"
      end
  11. Document API response formats

    master

    You can document the response of an API call using the returns statement. This is critical for auto-generating Swagger definitions. There are three primary formats:

    1. Reference a param-group: Use a previously defined :param_group name.
    2. Inline definition: Define the response structure directly within a do...end block.
    3. Array of objects: Use the :array_of key to describe a collection of items.

    If the :code argument is omitted, it defaults to 200.

  12. Requirements for releasing the apipie-rails gem

    master

    Before starting the release process, ensure you meet the following requirements:

    • Access: You must have push access to the GitHub repository https://github.com/Apipie/apipie-rails and push access to rubygems.org for the apipie-rails gem.
    • Dependencies: Install required system packages via sudo yum install python-slugify asciidoc.
    • Authentication: Ensure that git push and gem push do not require interactive authentication (e.g., use SSH keys or API keys). If interactive auth is required, perform these steps manually from the shell.
    • Verification: Ensure all CI checks have passed on the branch intended for release.