StrapiRuby
StrapiRuby is a Ruby wrapper around Strapi REST API, version 4. It has not been tested with previous versions.
Strapi is an open-source, Node.js based, Headless CMS to easily build customizable APIs.
I think it's one of the actual coolest solution for integrating a CMS into Rails for example, so let's dive in!
Table of contents
Installation
Add this line to your application's Gemfile:
gem "strapi_ruby"
Then if you use Rails, run in your terminal to generate a config initializer. Otherwise copy paste and fill the config block.
bundle
rake strapi_ruby:config
StrapiRuby.configure do |config|
config.strapi_server_uri = "http://localhost:1337/api"
config.strapi_token = "YOUR_TOKEN"
end
StrapiRuby.configure do |config|
config.strapi_server_uri = ENV["STRAPI_SERVER_URI"]
config.strapi_token = ENV["STRAPI_SERVER_TOKEN"]
end
IMPORTANT
- Always store sensible values in environment variables or Rails credentials
- Don't forget the trailing
/api
in your uri and don't finish it with a trailing slash.
And you're ready to fetch some data!
StrapiRuby.get(resource: :restaurants)
Usage
API
When passing most of the arguments and options, you can use either Symbol
or String
for single fields/items, and an Array
of Symbol
or String
.
API methods will return an OpenStruct which is similar to a Hash but you can access keys with dot notation. All fields of the OpenStruct have been recursively converted to OpenStruct as well so it's easy to navigate, as seen below
answer = StrapiRuby.get(resource: :articles)
answer = StrapiRuby.get(resource: "articles")
data = answer.data
meta = answer.meta
answer = StrapiRuby.get(resource: :articles, id: 2)
article = answer.data
title = article.attributes.title
data = answer.data
meta = answer.meta
error = answer.error.message
endpoint = answer.endpoint
.get
answer = StrapiRuby.get(resource: :restaurants)
StrapiRuby.get(resource: :restaurants, id: 1)
.post
StrapiRuby.post(resource: :articles,
data: {title: "This is a brand article",
content: "created by a POST request"})
.put
StrapiRuby.put(resource: :articles,
id: 23,
data: {content: "'I've edited this article via a PUT request'"})
.delete
StrapiRuby.delete(resource: :articles, id: 12)
.escape_empty_answer
See Graceful degradation
Basic Example: Rails
def home
@articles = StrapiRuby.get(resource: :articles)
end
# home.html.erb
<% StrapiRuby.escape_empty_answer(@articles) do %>
<ul>
<% @articles.data.each do |article| %>
<li>
<%= article.attributes.title %>
</li>
<% end %>
</ul>
<% end %>
Strapi Parameters
`strapi_ruby`` API functions wraps all parameters offered by the Strapi REST Api V4.
The query is built using a transverse hash function similar to Javascript qs
library used by Strapi.
Instead parameters should be passed as a hash to their key and you can use symbols instead of strings.
Only exceptions are for the operators
of the filters used as keys. Also, Integers, eg. for ID, must be passed as strings.
Full parameters documentation from Strapi is available here.
You can also use their interactive query builder. Just remember to convert the result correctly the resulting JS object to a hash with correct keys and values.
populate
StrapiRuby.get(resource: :articles, populate: :*)
StrapiRuby.get(resource: :articles, populate: [:categories])
StrapiRuby.get(resource: :articles, populate: { author: { populate: [:company] } })
StrapiRuby.get(resource: :articles, populate: [
"seoData",
"seoData.sharedImage",
"seoData.sharedImage.media",
])
StrapiRuby.get(resource: :articles, populate: {
testDZ: {
populate: :*,
},
})
StrapiRuby.get(resource: :articles, populate: {
testDz: {
on: {
"test.test-compo" => {
fields: [:testString],
populate: :*,
},
"test.test-compo2" => {
fields: [:testInt],
},
},
},
})
fields
StrapiRuby.get(resource: :articles, fields: :title)
StrapiRuby.get(resource: :articles, fields: [:title, :body])
sort
StrapiRuby.get(resource: :articles, sort: [])
StrapiRuby.get(resource: :articles, sort: ["createdAt:desc", "title:asc"])
filters
Use a String
and not a Symbol
when using operator
.
Operator | Description |
---|
$eq | Equal |
$eqi | Equal (case-insensitive) |
$ne | Not Equal |
$nei | Not Equal (case-insensitive) |
$lt | Less than |
$lte | Less than (case-insensitive) |
$gt | Greater than |
$gte | Greater than (case-insensitive) |
$in | In |
$notIn | Not in |
$contains | Contains |
$notContains | Does not contain |
$containsi | Contains (case-insensitive) |
$notContainsi | Does not contain (case-insensitive) |
$null | Is null |
$notNull | Is not Null |
$between | Is between |
$startsWith | Starts with |
$startsWithi | Starts with (case-insensitive) |
$endsWith | Ends with |
$endsWithi | Ends with (case-insensitive) |
$or | Or |
$and | And |
$not | Not |
StrapiRuby.get(resource: :users, filters: { username: { "$eq" => "John" } })
StrapiRuby.get(resource: :restaurants,
filters: {
id: {
"$in" => ["3", "6", "8"],
},
})
StrapiRuby.get(resource: :books,
filters: {
"$or" => [
{
date: {
"$eq" => "2020-01-01",
},
},
{
date: {
"$eq" => "2020-01-02",
},
},
],
author: {
name: {
"$eq" => "Kai doe",
},
},
})
StrapiRuby.get(resource: :restaurants,
filters: {
chef: {
restaurants: {
stars: {
"$eq" => 5,
},
},
},
})
Only one pagination method is possible.
StrapiRuby.get(resource: :articles, page: 1, page_size: 10)
Only one pagination method is possible.
StrapiRuby.get(resource: :articles, start: 0, limit: 10)
locale
I18n plugin should be installed.
StrapiRuby.get(resource: :articles, locale: :fr)
publication_state
Use :preview
or :live
StrapiRuby.get(resource: :articles, publication_state: :preview)
Use raw query
If you wanna pass a raw query you decide to build, just use raw as an option.
It cannot be combined with any other Strapi parameters.
StrapiRuby.get(resource: articles:, raw: "?fields=title&sort=createdAt:desc")
Configuration
You can pass more options via the config block.
Show Endpoint
This option is for accessing the resulting endpoint in a successful error, ie. strapi_server_uri
+ its query.
It defaults to false
.
StrapiRuby.configure do |config|
config.show_endpoint = true
end
StrapiRuby.get(resource: :articles, show_endpoint: true)
StrapiRuby.get(resource: :articles, show_endpoint: true).endpoint
Or directly in the options parameters
DateTime Conversion
By default, any createdAt
, publishedAt
and updatedAt
fields in the answer will be recursively converted to DateTime
instances, making it easy to use #strftime
method.
But if you don't want this conversion, pass it to the configure block.
StrapiRuby.configure do |config|
config.convert_to_datetime = false
end
Markdown Conversion
Selected fields will automatically be converted to HTML using redcarpet
gem. This is very useful to get data ready for the views.
StrapiRuby.configure do |config|
config.convert_to_html = [:body]
end
StrapiRuby.get(resource: :articles, fields: :body, convert_to_html: [:body])
Faraday Block
Passing a Proc
You can pass a proc when configuring Strapi Ruby just as you'd pass a block when creating a new instance of a Faraday.
Check Faraday documentation
StrapiRuby.configure do |config|
config.faraday = Proc.new do |faraday|
faraday.headers['X-Custom-Header'] = 'Custom-Value'
end
end
Default Faraday::Connection used by the gem
Default options used by this gem are url_encode
and Faraday.default_adapter
, but you can override them.
Default headers cannot be overriden but will be merged with your added configuration.
default_headers = { "Content-Type" => "application/json",
"Authorization" => "Bearer #{strapi_token}",
"User-Agent" => "StrapiRuby/#{StrapiRuby::VERSION}" }
Handling Errors
Depending on your utilisation, there are multiple ways to handle errors.
Errors Classes
class ConfigurationError < StandardError
class ClientError < StandardError
class ConnectionError < ClientError
class UnauthorizedError < ClientError
class ForbiddenError < ClientError
class NotFoundError < ClientError
class UnprocessableEntityError < ClientError
class ServerError < ClientError
class BadRequestError < ClientError
class JSONParsingError < ClientError
Graceful degradation
One way to handle errors and gracefuly degrade is using .escape_empty_answer
and use a block to nest your data accessing code.
Errors will still be logged in red in console.
Definition
module StrapiRuby
def escape_empty_answer(answer)
return answer.error.message if answer.data.nil?
yield
end
end
Example : Usage in a Rails view
<% StrapiRuby.escape_empty_answer(answer) do %>
<%= answer.title %>
<%= answer.body %>
<% end %>
Or you may want to handle specific errors like this:
begin
answer = StrapiRuby.get(resource: "articles")
rescue NotFoundError e =>
rescue ClientError e =>
end
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/saint-james-fr/strapi_ruby. This project is intended to be a safe, welcoming space for collaboration.
Tests
Run bundle exec rspec
to run the tests.
Inside spec/integration.rb
you'll have access to integration tests.
You'll need to configure environment variables within the repo and run a strapi server to run these tests sucessfully.
See Strapi documentation for more details about installing a Strapi Server here
License
The gem is available as open source under the terms of the MIT License.