Rack::Cargo
Have you built nice RESTful APIs? I believe you have.
Then you are also familiar with the situation, where API consumer needs to perform multiple actions at once. Maybe in the client application, multiple resources get created on one page. Creating multiple resources means making multiple HTTP requests.
What if you could batch the requests together and send in one HTTP requests, wouldn't that be more efficient? I believe it would be! That's where Rack::Cargo comes in.
Figuratively speaking, load your HTTP-request ship with the request cargo and put it on the way and enjoy your RESTful API! ☀️
You: I want to know more about RESTful. Where should I look?
Me: Cool! I recommend this awesome talk: In Relentless Pursuit of REST by Derek Prior
Installation
Add this line to your Rack-based application's (Rails, Sinatra, etc.) Gemfile:
gem 'rack-cargo'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rack-cargo
Usage
Regarding individual requests, rack-cargo
tries to follow the Rack SPEC. If you find any mismatches, feel free to open an issue or a pull request.
Configuration
Initialize the middleware:
Rack::Cargo.configure do |config|
config.batch_path = '/batch'
config.timeout = 1
end
Instruct rack
to use the middleware:
use Rack::Cargo::Middleware
Referencing requests
Requests in batch have access to the responses of executed (named) requests. This is useful, when creating resources and using the reference to it in the same batch.
References can be used in path
and body
elements.
Example shows both usages with order.uuid
from the order
response:
// This is batch request payload:
{
"requests": [
{
"name": "order",
"path": "/orders",
"method": "POST",
"body": {
"address": "Home, 12345"
}
},
{
"name": "order_item",
"path": "/orders/{{ order.uuid }}/items", // <-- here
"method": "POST",
"body": {
"title": "A Book"
}
},
{
"name": "payment",
"path": "/payments",
"method": "POST",
"body": {
"orders": [
"{{ order.uuid }}" // <-- and here
]
}
}
]
}
// This is a possible response:
[
{
"name": "order", // <-- "order" part of "order.uuid"
"status": 201,
"headers": {},
"body": {
"uuid": "bf52fdb5-d1c3-4c66-ba7d-bdf4cd83f265", // <-- "uuid" part of "order.uuid"
"address": "Home, 12345"
}
},
{
"name": "order_item",
"status": 201,
"headers": {},
"body": {
"uuid": "38bc4576-3b7e-40be-a1d6-ca795fe462c8",
"title": "A Book"
}
},
{
"name": "payment",
"status": 201,
"headers": {},
"body": {
"uuid": "c4f9f261-7822-4217-80a2-06cf92934bf9",
"orders": [
"bf52fdb5-d1c3-4c66-ba7d-bdf4cd83f265"
]
}
}
]
Modifying batch processing pipeline
Batch processing is composed of steps that perform some concrete action on the request and/or state of the processing.
To insert processor in the pipeline, define the processor and inject it to the processors list:
module MyFeeder
def self.call(request, state)
# calculate something
state.store(:data, "Useful data to MyEater")
end
end
module MyEater
def self.call(request, state)
data = state.fetch(:data)
# do something with the data
end
end
Rack::Cargo.configure do |config|
config.processors.insert(2, MyFeeder) # insert into third position
config.processors.insert(3, MyEater) # insert into fourth position
end
Now your processors will be included in the pipeline.
Individual request timeout
Individual requests in batch will be dropped after execution exceeds configured timeout. Default timeout is 1 second.
Specify timeout for single request in configuration:
Rack::Cargo.configure do |config|
config.timeout = 1
end
Timed out requests' response is with status 504 (Gateway timeout) and with empty headers, body.
nil
body in an individual request
In case an individual requests' body is nil
, it will be sent downstream as ""
(empty String
).
No body in an individual response
In case an individual request receives a response without a body (as empty String
), it will be sent upstream as nil
.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/murdho/rack-cargo. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Rack::Cargo project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.