Rails Plugin for quick intelligent API Creation by Tuitu Technology Solutions
- Add To Gemfile:
gem 'rest_rails'
- Bundle Install:
bundle install
- Install RestRails:
rails g rest_rails:install
- 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) - Change '/api/v1' to whatever naming structure you would like to prepend REST API routes.
- 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
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:
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:
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 |
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' |
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"
}
]
}
}
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>
}
}
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>
}
}
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.
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"
}
]
}
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
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"
}
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"
}
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.