Skip to content

(GEM) Build a Rails REST-API in 2-minutes

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
MIT-LICENSE
Notifications You must be signed in to change notification settings

sergio-rivas/rest-rails

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RestRails

Rails Plugin for quick intelligent API Creation by Tuitu Technology Solutions

How To Start

Step 1: Basic Setup

  1. Add To Gemfile: gem 'rest_rails'
  2. Bundle Install: bundle install
  3. Install RestRails: rails g rest_rails:install

Step 2: Change Mounted Path (optional)

  1. Make sure the line mount RestRails::Engine => '/api/v1', as: 'rest' is at the bottom of your routes. (This will prevent RestRails from taking over any custom API routing you might do)
  2. Change '/api/v1' to whatever naming structure you would like to prepend REST API routes.

Step 3: Whitelist Permitted Columns (optional)

  1. For better security, it is strongly recommended to setup the initializer to whitelist your columns.

Here is an example on how to whitelist columns for a sample database w/ three tables: articles, comments, users. In this example, we only want the API to allow REST API endpoints for articles and comments, but not users.

Note: table_names are keys. Acceptable values include: (:all, :none, or an Array of column_names)

RestRails.configure do |config|
  # ...
  config.permit = {
    articles: :all,
    users: :none,
    comments: [:content]
  }
end

The Basics

RestRails will automatically create a base API for all standard CRUD actions. Just one line of code to implement a powerful REST API.

For Example's sake, let's take the following schema:

  • articles: title, description, content
  • comments: article_id, content

Further more, as per activestorage convention, Article has_one_attached :feature_image & has_many_attached :content_images

Let's say we mount RestRails at /api/v1, the Following routes are included for articles:

Articles

REST action method route notes
index GET /api/v1/articles index paginated by 100
show GET /api/v1/articles/:id show for one article
create POST /api/v1/articles create new article
update PATCH /api/v1/articles/:id update an article
destroy DELETE /api/v1/articles/:id destroy an article
fetch_column GET /api/v1/articles/:id/title fetch title of article
fetch_column GET /api/v1/articles/:id/description fetch description of article
fetch_column GET /api/v1/articles/:id/content fetch content of article
attach POST /api/v1/articles/:id/attach/feature_image attach file to feature_image
attach POST /api/v1/articles/:id/attach/content_images attach file to content_images

And the following for comments:

Comments

REST action method route notes
index GET /api/v1/comments index paginated by 100
show GET /api/v1/comments/:id show for one comment
create POST /api/v1/comments create new comment
update PATCH /api/v1/comments/:id update an comment
destroy DELETE /api/v1/comments/:id destroy an comment
fetch_column GET /api/v1/comments/:id/article_id fetch article_id of comment
fetch_column GET /api/v1/comments/:id/content fetch content of comment

Using the REST API

Index

GET '/articles' will return a JSON response as follows:

{
  "code": 200,
  "objects": [
    {
      "id": 1,
      "created_at": "2019-08-27T08:22:19.357Z",
      "updated_at": "2019-08-27T08:22:19.499Z",
      "title": "Discourse on Dystopian Non Fiction",
      "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
      "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's...",
      "feature_image": {
        "attachment_id": 1,
        "url": "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsib--f573ab9452c272881fb/jopmqH0.jpg"
      },
      "content_images": [
        {
          "attachment_id": 3,
          "url": "http://localhost:3000/rails/active_storage/blobs/sjfioadifo-8fdjsfaj/fjso.jpg"
        }, {
          "attachment_id": 12,
          "url": "http://localhost:3000/rails/active_storage/blobs/fjiods--k0f09fs/jfdsjk.jpg"
        }
      ]
    },
    {...},
    {...},
    ...
  ],
  "count": 100,
  "total": 1300
}

The index REST API is by default paginated by 100. To go through pages, add the following params to the GET path:

page=<number>

ex: '/api/v1/articles?page=2'

Models can also be filtered by a very basic WHERE query param structure.

For Article, the index API point can receive the following paramters:

param type example notes
page Integer page=2 Will paginate by 100 per page.
article[title] String article[title]=Some+Title Will match articles with titles same as the value.
article[title][] Array article[title][]=SomeTitle&article[title][]=SomeOtherTitle Will match articles with title of 'SomeTitle' OR 'SomeOtherTitle'
article[description] String article[description]=Some+Description Will match articles with descriptions same as the value.
article[description][] Array article[description][]=SomeDescription&article[description][]=SomeOtherDescription Will match articles with description of 'SomeDescription' OR 'SomeOtherDescription'
article[content] String article[content]=Some+Content Will match articles with contents same as the value.
article[content][] Array article[content][]=SomeContent&article[content][]=SomeOtherContent Will match articles with content of 'SomeContent' OR 'SomeOtherContent'

Show

GET '/articles/1' will return a JSON response as follows:

{
  "code": 200,
  "object": {
    "id": 1,
    "created_at": "2019-08-27T08:22:19.357Z",
    "updated_at": "2019-08-27T08:22:19.499Z",
    "title": "Discourse on Dystopian Non Fiction",
    "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
    "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's...",
    "feature_image": {
      "attachment_id": 1,
      "url": "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsib--f573ab9452c272881fb/jopmqH0.jpg"
    },
    "content_images": [
      {
        "attachment_id": 3,
        "url": "http://localhost:3000/rails/active_storage/blobs/sjfioadifo-8fdjsfaj/fjso.jpg"
      }, {
        "attachment_id": 12,
        "url": "http://localhost:3000/rails/active_storage/blobs/fjiods--k0f09fs/jfdsjk.jpg"
      }
    ]
  }
}

Create

The create paths enforce Rails strong params. So only properly structured requests will be allowed. POST '/articles' can accept a payload in the following structure:

  {
    "article" {
      "title": "Discourse on Dystopian Non Fiction",
      "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
      "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's..."
    }
  }

If successful (and passes your ActiveRecord validations), the response will be as follows:

{
  "code": 200,
  "msg": "success",
  "object": {
    "id": 1,
    "created_at": "2019-08-27T08:22:19.357Z",
    "updated_at": "2019-08-27T08:22:19.357Z",
    "title": "Discourse on Dystopian Non Fiction",
    "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
    "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's...",
    "feature_image": null,
    "content_images": []
  }
}

Note: If you are using forms to submit data w/ attachments via activestorage, you can also add attachments to the payload sent, as long as it matches the naming of your activestorage attachment, and the form submission content-type is multipart/form_data.

  {
    "article" {
      "title": "Discourse on Dystopian Non Fiction",
      "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
      "feature_image": <uploaded_file>
    }
  }

Update

The update paths enforce Rails strong params. So only properly structured requests will be allowed. PATCH '/articles/1' can accept a payload in the following structure (with one or more columns to be updated):

  {
    "article" {
      "title": "Discourse on Dystopian Non Fiction",
      "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
      "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's..."
    }
  }

If successful (and passes your ActiveRecord validations), the response will be as follows:

{
  "code": 200,
  "msg": "success",
  "object": {
    "id": 1,
    "created_at": "2019-08-27T08:22:19.357Z",
    "updated_at": "2019-08-27T08:22:19.357Z",
    "title": "Discourse on Dystopian Non Fiction",
    "description": "This is an abstract description used to mislead readers into clicking on the article and take a deeper read.",
    "content": "Wow, the reader actually clicked! Now let me brainwash him with this heavily opinionated article based on a myriad of unverified sources with the credibility of a personified M&M's...",
    "feature_image": null,
    "content_images": []
  }
}

Note: If you are using forms to submit data w/ attachments via activestorage, you can also add attachments to the payload sent, as long as it matches the naming of your activestorage attachment, and the form submission content-type is multipart/form_data.

  {
    "article" {
      "title": "Discourse on Dystopian Non Fiction",
      "feature_image": <uploaded_file>
    }
  }

Destroy

DELETE '/articles/1' only needs the ID number.

If successful (and passes your ActiveRecord validations), the response will be as follows:

{
  "code": 200,
  "msg": "success"
}

Note: If you are using activestorage, the destroy process will also automatically destroy attachments from your bucket.

Fetch Column

GET '/articles/1/title'

If column exists, the response will be as follows:

{
  "code": 200,
  "msg": "success",
  "value": "Discourse on Dystopian Non Fiction"
}

Note: If you are using activestorage, you will return the following for has_one_attached:

{
  "code": 200,
  "msg": "success",
  "value": {
    "attachment_id": 1,
    "url": "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsib--f573ab9452c272881fb/jopmqH0.jpg"
  }
}

And for has_many_attached:

{
  "code": 200,
  "msg": "success",
  "value": [
    {
      "attachment_id": 3,
      "url": "http://localhost:3000/rails/active_storage/blobs/sjfioadifo-8fdjsfaj/fjso.jpg"
    },
    {
      "attachment_id": 12,
      "url": "http://localhost:3000/rails/active_storage/blobs/fjiods--k0f09fs/jfdsjk.jpg"
    }
  ]
}

Activestorage Attachments

For activestorage attachment support, the following two routes are added to models using activestorage:

/table_name/:id/attach/:attachment_name and /table_name/:id/unattach/:attachment_id

Attach

The routes generated for the rest API are based on the naming provided in your ActiveRecord model when using activestorage.

  • Supports both has_one_attached & has_many_attached.

In the articles example above, this would be:

POST "/api/v1/articles/attach/feature_image"

The payload structure in this case needs only to be:

{
  attachment: <file_uploaded>
}

If successful, the response will be as follows:

{
  "code": 200,
  "msg": "success"
}

Unattach

DELETE '/table_name/:id/unattach/:attachment_id'

* Note, Response will fail if the attachment_id provided does not belong to the object.

If successful, the response will be as follows:

{
  "code": 200,
  "msg": "success"
}

Contribution

Here are some features in need of development.

  • Support different popular attachment gems.
  • Add Locale fetching based on page-routes.
  • Add standarized testing rspecs.
  • Add support for switching activestorage sources
  • Add support to allow user to provide any before_actions wanted.