Restforce: a Ruby client for the Salesforce REST API with OAuth refresh built in
A Ruby client for the Salesforce REST API.
At a glance
- What is it?
- Restforce is a Ruby gem that wraps the Salesforce REST API, returning Hashie::Mash objects so responses are navigable without digging through hashes. It covers OAuth with automatic token refresh, username and password, JWT bearer, the Streaming, Composite and Composite Batch APIs, aggregate queries and blob data types, and it needs Ruby 3.1 or newer from version 8.
- Who is it for?
- Restforce fits a Ruby team integrating with Salesforce who wants Mash-style responses, an OAuth middleware that refreshes tokens without a hand-rolled retry loop, and the Composite and Streaming APIs available without writing raw HTTP. It does not fit a project on Ruby 3.0 or older, since version 8 requires Ruby 3.1 and the older lines are pinned to old gem versions.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 90 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why the responses are Mash objects
Restforce is a Ruby gem for the Salesforce REST API, and the design decision that shapes everything else is that API calls return Hashie::Mash objects by default.
The consequence is that a query result is navigable rather than a hash of hashes. The documentation gives the example directly:
client.query('select Id, (select Name from Children__r) from Account').first.Children__r.first.NameA parent-to-child relationship is traversed with chained method calls rather than bracket lookups, and the query in that example is a subquery against a child relationship, so the same style covers both the flat and the nested case.
The architecture behind that is described as clean and modular, built on Faraday middleware with Hashie::Mash responses. Faraday is the HTTP layer that most Ruby HTTP libraries are built around, and putting the client on middleware means the request pipeline is where retry, authentication and compression live rather than being mixed into the calling code.
The feature list is long and specific. It covers multiple users from different organisations, parent-to-child relationships, aggregate queries, blob data types, dependent picklists, GZIP compression, custom Apex REST endpoints, decoding Force.com Canvas signed requests, and support for the GetUpdated API. The last of those, Canvas signed request decoding, is marked as new in the documentation.
Three ways to authenticate, and which one fits
The documentation's guidance on authentication is about your use case rather than about the API, and it is worth following because the three methods have very different operational shapes.
OAuth token authentication is for an application where many users from different organisations authenticate through OAuth and you need to act on their data in their org. The simple form takes an access token, an instance URL and an API version:
client = Restforce.new(oauth_token: 'access_token',
instance_url: 'instance url',
api_version: '41.0')The form you will actually use adds the refresh token, the client credentials and a callback:
client = Restforce.new(oauth_token: 'access_token',
refresh_token: 'refresh token',
instance_url: 'instance url',
client_id: 'client_id',
client_secret: 'client_secret',
authentication_callback: Proc.new { |x| Rails.logger.debug x.to_s },
api_version: '41.0')With that in place the middleware uses the refresh token automatically when the access token is invalid, and the callback receives the Salesforce response so you can persist the new token. The documentation explains why that matters: without saving it, every subsequent call repeats the cycle of auth failure, issue new token, auth success. The callback is passed a Mash of the response, which carries the access token, the signature, the scope, the instance URL, the id, the token type and the issued at timestamp. The id field is the one to store if you need to know which user a token pair belongs to.
Username and password authentication is the choice for a single organisation, the documentation suggests, such as an internal Salesforce integration. The client takes the username, password, instance URL, an optional host for sandbox orgs, the client key and secret, and an API version.
The third method is a JWT bearer token, which takes the username, client id, instance URL, a jwt_key holding the certificate private key, and an API version.
One caveat appears under all of them: the client object should not be reused across different threads, because of thread-safety issues. That is a real constraint for a web application, and it means one client per thread rather than one client per process.
Version support tied to Ruby versions
Since version 8.0.0, the gem is only compatible with Ruby 3.1.0 and later, and the README lays out the fallback for older interpreters.
For Ruby 3.0, use version 7.6.0 or earlier. For Ruby 2.7, version 6.2.4 or earlier. For Ruby 2.6, version 5.3.1 or earlier. For Ruby 2.5, version 5.0.6 or earlier. For Ruby 2.4, version 4.3.0 or earlier. For Ruby 2.3, version 3.2.0 or earlier. For Ruby 2.2, 2.1 and 2.0, version 2.5.3 or earlier, and for Ruby 1.9.3, version 2.4.2.
That table is long because the gem has been around long enough to span a decade of Ruby releases, and it is worth reading as a maintenance signal as much as a compatibility table. A library that has shipped a version for every Ruby from 1.9.3 to 3.1 is a library that has been maintained for a long time, and the release history backs that up: v8.0.0 and v7.6.0 were both published on 2024-12-18, and the patch release v8.0.1 came in December 2025.
The gem is versioned using Semantic Versioning, so the project commits that there will be no breaking changes outside a major version, with the MAJOR.MINOR.PATCH convention meaning a move from 3.1.0 to 4.0.0 is allowed to include incompatible API changes.
The installation itself is the standard Bundler path. The README says to add the line to your application's Gemfile:
gem 'restforce', '~> 8.0.1'then execute bundle, or install it directly with gem install restforce.
The APIs beyond plain queries
Beyond the basics, the feature list names several Salesforce APIs that need dedicated handling rather than a generic request.
The Streaming API support covers long-lived event subscriptions, which is the piece most REST clients cannot offer at all. The Composite API is for batching several operations into one request, and the Composite Batch API is for a batch of subrequests, where a partial failure can be handled rather than failing the whole batch. The GetUpdated API is for retrieving records changed since a given timestamp, and blob data types are supported for the binary fields Salesforce stores outside the normal record payload.
Aggregate queries are supported as well, which matters because they return a nested result shape rather than a list of records, and a client that does not understand that shape returns something awkward to work with.
Custom Apex REST endpoints are supported, so you can call the REST endpoints your own organisation code exposes rather than being limited to the standard objects.
The dependency on Faraday is what makes several of these features reasonable to implement, because the request pipeline is where a streaming reader or a compression layer attaches.
The development setup and its contradictions
The repository is a standard Ruby gem layout with a lib directory, a spec directory, a Rakefile, a Gemfile, a restforce.gemspec, a CHANGELOG, a CONTRIBUTING guide, a CODE_OF_CONDUCT, an UPGRADING document and a Guardfile for the interactive test loop.
The linting setup is RuboCop with both a .rubocop.yml and a .rubocop_todo.yml, where the todo file is the usual place for existing violations that are not yet fixed.
The Docker setup is where the interesting inconsistency sits. The docker-compose file defines a single service built from the repository Dockerfile, with the working tree mounted at /srv, and the image is tagged restforce:dev. The Dockerfile itself is based on ruby:2.6.5-alpine, which is a Ruby version that the gem no longer supports from version 8 onward.
That does not necessarily mean the container is broken, since the gemspec may still permit older interpreters for the 7.x line, but it does mean the development container and the current release target different Ruby versions. Anyone picking this up should check whether the container still runs the test suite as written, or whether the base image needs bumping to match the supported range.
The rest of the Dockerfile is conventional. It installs certificates, wget, openssl, bash, build tools, git, sqlite development headers, timezone data and tini, sets the locale environment to en_US.UTF-8, pins bundler to 2.1.4, runs bundle install, and sets the entrypoint through tini with the command running the specs. Using tini as the init process is the right call for a container whose entrypoint is a process supervisor.
Continuous integration runs on CircleCI, and the repository is MIT licensed.
Where Restforce is the wrong choice
The first thing to weigh is whether you want a Ruby client at all. Salesforce publishes its own tooling, and the REST API is documented directly, so a thin HTTP client of your own is entirely feasible for a project with a handful of endpoints. What Restforce buys is the token refresh middleware, the Mash response shape, and the Composite and Streaming support, which is real value once you need it.
The second limitation is the same one it handles honestly, which is thread safety. The client should not be shared across threads, so in a threaded web server you need one client per thread, and each client holds an OAuth token and its refresh state. That is a design constraint that will shape how you structure the connection to Salesforce in your application, and it is the kind of thing that is easy to get wrong once and hard to debug afterwards.
The third is that the gem pins an API version per client. The examples use 41.0, 55.0 and 38.0 in different places, which is fine, but it means the Salesforce API version you talk to is a deliberate choice you have to keep track of, and the client does not follow a default that moves under you.
For teams on Ruby 3.0 or older the answer is simply the previous line of the gem, which is a reasonable fallback but a maintenance liability, since 7.6.0 predates the current major version and its own fixes.
The comparison with other Salesforce clients is straightforward: if your team is on Node or Python, a client in that ecosystem will fit better than a Ruby gem reached through a service boundary, and the Mash convenience is worth less when it is not your language's idiom.
Editorial conclusion
Restforce fits a Ruby team integrating with Salesforce who wants Mash-style responses, an OAuth middleware that refreshes tokens without a hand-rolled retry loop, and the Composite and Streaming APIs available without writing raw HTTP. It does not fit a project on Ruby 3.0 or older, since version 8 requires Ruby 3.1 and the older lines are pinned to old gem versions. Before adopting it, choose the authentication method that matches whether you serve one organisation or many, set the api_version explicitly rather than relying on a default, create one client per thread rather than sharing a client across threads, and pin the gem version, since the project follows Semantic Versioning and its major versions line up with Ruby version support.
Frequently asked questions
What is Restforce and what does it return from API calls?
Restforce is a Ruby gem for the Salesforce REST API, built on Faraday middleware with Hashie::Mash responses. By default every API call returns Mash objects, so a query result is navigated with chained calls such as client.query(...).first.Children__r.first.Name rather than nested hash lookups.
How do I install Restforce and which Ruby versions does it support?
Add `gem 'restforce', '~> 8.0.1'` to your Gemfile and run bundle, or install it with gem install restforce. As of version 8.0.0 the gem requires Ruby 3.1.0 or later, with older lines pinned per interpreter, so Ruby 3.0 uses 7.6.0 or earlier and Ruby 2.7 uses 6.2.4 or earlier.
How does Restforce handle OAuth token refresh?
You pass refresh_token, client_id, client_secret and an authentication_callback alongside the access token, and the middleware uses the refresh token automatically when the access token is invalid. The callback receives the Salesforce response as a Mash so you can persist the new access token instead of repeating the auth failure cycle on every call.
Which Salesforce APIs does Restforce support?
It supports the Streaming API, the Composite API, the Composite Batch API, the GetUpdated API, aggregate queries, blob data types, dependent picklists, custom Apex REST endpoints, GZIP compression and Force.com Canvas signed request decoding. Multiple users from different organisations and parent-to-child relationships are also supported.
Can I share one Restforce client across threads?
No. The documentation warns that the client object should not be reused across different threads because of thread-safety issues, so you need one client per thread. The username and password option also takes an optional host, which is test.salesforce.com for sandbox organisations.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/restforce-restforce)Was this useful?