Rails 6 had added the support for multiple database connections using which we can configure the separate databases for reads and writes.
For example, we have two databases write_database
and read_database
.
Now, in order to use the above-defined databases, we need to set up the Active Record model.
To enable the automatic switching between the databases based on the HTTP Verb, we need to add the below configuration:
Using the above configuration,
Rails looks for the reading_request?
method
defined in the middleware ActiveRecord::Middleware::Middleware::DatabaseSelector
.
Its default implementation is true for GET
and HEAD requests,
which means for POST, PUT, DELETE,
or PATCH request,
the application will automatically write to the write_database
and for read, it will use read_database
.
Note:- According to the
docs
we are supposed to add the above configuration in
initializers(/config/initializers/multi_db.rb)
but this is broken in Rails latest release so as suggested here in the
Rails issue,
we need to add the config in application.rb
Before
Let us call a create_product
(POST) API to create a Product.
So, as expected, the product is created successfully in write_database
.
If we call a get_product
(GET) API,
it says product not found because,
the GET request will be redirected to read_database
,
and that does not have a Product that we created recently.
So, this is working as expected. Now suppose, if we call a GraphQL API to read the particular product.
Whoa!!! We get the Product details back.
Ideally, it should respond with a not_found
error
because it is a read request,
but GraphQL API uses the POST HTTP method
and
according to the default implementation POST requests get redirected to the write_database
.
After
To fix this,
Rails has moved reading_request?
method from the ActiveRecord::Middleware::Middleware::DatabaseSelector
to the ActiveRecord::Middleware::DatabaseSelector::Resolver
class,
so that we can override
and create custom Resolver.
Let us create a custom resolver
and override the reading_request?
method to add the validations for a GraphQL API.
So, our custom reading_request?
checks if the request is POST,
type is GraphQL and params doesn’t have mutation
then consider it as a read request
and redirect it to the read_database
.
Now, if we call a GraphQL API again to read the particular product.
It responds with a not_found
message,
which is expected because read_database
doesn’t have a Product record.
Note: The enhancement is yet to be released in the official Rails version
Check out the PR for more details.