Turn your Ruby on Rails REST API to GraphQL using Hasura Actions
Tags: ruby, rails, hasura, graphql
Published: 2020-08-23 17:09:39

---
title: Turn your Ruby on Rails REST API to GraphQL using Hasura Actions
published: true
date: 2020-08-21 12:21:47 UTC
tags: ruby, rails, hasura, graphql
canonical_url: https://hasura.io/blog/turn-your-ruby-on-rails-rest-api-to-graphql-using-hasura-actions/
---

_This post is a part of a series which goes over how to convert an existing REST API to GraphQL by defining types and configuring endpoints._

![](https://hasura.io/blog/content/images/2020/08/image-4.png)

[Hasura](https://hasura.io/) gives you instant [GraphQL](https://hasura.io/graphql/) CRUD for databases (currently Postgres) which should cover most of the data fetching and real-time subscription use cases. However, sometimes we want to add custom business logic to our API. There are different ways of doing this with Hasura:

_Remote schemas_: If you have an existing GraphQL server or you're comfortable writing one, you can add this GraphQL server as a [Remote schema](https://hasura.io/docs/1.0/graphql/manual/remote-schemas/index.html) and Hasura merges it automatically.

_Actions_: If you'd like to write a new or keep your existing REST API for custom business logic, [Hasura actions](https://hasura.io/docs/1.0/graphql/manual/actions/index.html) are the way to go. Actions are a way to extend Hasura’s schema with custom business logic using custom queries and mutations. Actions can be added to Hasura to handle various use cases such as data validation, data enrichment from external sources and any other complex business logic.

In this post, we will focus on [Hasura actions](https://hasura.io/docs/1.0/graphql/manual/actions/index.html) and we'll see how to use them to convert a Ruby on Rails REST API to GraphQL.

## Create a Hasura Cloud Project

Before we begin, let's create a project on [Hasura Cloud](https://hasura.io/cloud/) to set up the action. Click on the `Deploy to Hasura` button below, sign up for free and create a new project.

<a target="_blank" rel="noopener" href="https://cloud.hasura.io"><img src="https://camo.githubusercontent.com/a6de317cd7d0ed4e8722684b428f72e3da614fe8/68747470733a2f2f6772617068716c2d656e67696e652d63646e2e6861737572612e696f2f696d672f6465706c6f795f746f5f6861737572612e706e67"></a>

Hasura requires a Postgres database to start with. We can make use of Heroku's free Postgres database tier for the purpose of this app.

![](https://hasura.io/blog/content/images/2020/07/create-project-heroku1.png)<figcaption>Hasura Cloud Database Setup</figcaption>

After signing in with Heroku, you should see the button `Create project`.

![Heroku Database Deployment on Hasura Cloud](https://hasura.io/blog/content/images/2020/07/Screenshot-2020-07-14-at-3.28.18-PM.png)<figcaption>Heroku Database Provisioning via Hasura Cloud</figcaption>

Once the project creation is done, click on the `Launch the Console` button on the [`Projects`](https://cloud.hasura.io/projects) tab for the newly created project and the Hasura Console should be visible. By creating this project, you have already got a GraphQL endpoint for the Postgres database that is globally available, secure and scalable from the beginning.

## Create an action

Now that we have our Hasura project up and running, let's create an action by heading to the `Actions` tab on the Hasura console and then clicking the `Create` button.

Our action will look as follows:

![](https://hasura.io/blog/content/images/2020/07/Screenshot-2020-07-14-at-3.33.38-PM.png)

### Action Definition

```
type Mutation {
  registerUser (
    name: String!
    email: String!
    password: String!
  ): UserOutput
}
```

### Types Definition

```
type UserOutput {
  id : uuid!
}
```

We just defined a new mutation type called `registerUser` which accepts the `name`, `email` and `password` arguments and returns the `id` of the user.

We can add the correct handler URL later. For now, we can just click the `Create` button to create the action.

Let's verify if the custom mutation was generated by heading to the GraphiQL tab and trying it out:

```
mutation {
  registerUser(name: "Marion", email: "marion@hasura.io", password: "mysecretpassword") {
    id
  }
}
```

You will get the following `http` exception:

![](https://hasura.io/blog/content/images/2020/08/image.png)

The reason for the error is that the handler URL has not been configured yet. But this test confirms that the custom mutation was successfully generated based on the types defined to query on the same GraphQL endpoint.

Now let's go ahead and actually define the Ruby on Rails app, so that this mutation will eventually succeed.

### Codegen: Auto generate boilerplate code

Back on the `Actions` tab and on our newly created action, click on the `Codegen` tab to auto-generate the code for our Ruby on Rails server. Since we are generating the API from scratch, we need the full server setup instead of just the `POST` handler.

![](https://hasura.io/blog/content/images/2020/08/image-1.png)

### Business logic required

The following business logic will need to be added in the handler for this action:

- Receive the action arguments **name** , **email** and **password** on `request` **.**
- Convert the plaintext password input into a hashed secure password.
- Send a mutation to Hasura to save the newly created user with the hashed password.
- Return the created user object to signal success, or else error.

### Run the action

The generated codegen (`registerUser.rb`) can be copied to your filesystem to make it possible to make the necessary changes to the handler.

```
begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"
  gem 'rails', '~> 6.0.0'
end

require "action_controller/railtie"

class App < Rails::Application
  routes.append do
    post "/registerUser" => "hasura#register_user_handler"
  end

  config.consider_all_requests_local = true # display errors
end

class HasuraController < ActionController::API
  def register_user_handler
    request_data = params[:input]
    puts request_data
    render json: request_data
  end
end

App.initialize!
Rack::Server.new(app: App, Port: 3000).start
```
<figcaption>registerUser.rb</figcaption>

Once you copy the codegen, you can set up Rails and start running the server:

1. If you haven't already, i[nstall Ruby](https://www.ruby-lang.org/en/documentation/installation/) 
2. Run the Ruby server:

```
ruby registerUser.rb
```

This will first install all the Ruby gems and will then start up a server that is running on port 3000.

![](https://hasura.io/blog/content/images/2020/08/image-2.png)<figcaption>Ruby development server</figcaption>

With this, we need to update our handler URL for the action so that the HTTP call works. The `registerUser` endpoint needs to be added and exposed, so that the handler URL will look as follows `http://localhost:3000/registerUser`.

Since we need a way to communicate with our Ruby server from the Hasura Cloud instance, we need to expose the Ruby server on a public URL. We can host this server on a cloud instance and point to that URL/IP. But for brevity of this tutorial, we can use `ngrok`, which gives public URLs for exposing local servers, so that they can be contacted by a cloud instance.

Once you have `ngrok` [installed](https://gist.github.com/wosephjeber/aa174fb851dfe87e644e#file-ngrok-installation-md), in a different tab, you can run the following command:

```
ngrok http 3000
```

This should expose your local Ruby server running on port 3000 on a public URL (something like `http://b4318819ea61.ngrok.io`).

Go back to the `Modify` tab of the `registerUser` action that we created earlier on the Hasura Console. Update the handler URL to the above one.

It would look like `http://b4318818ea61.ngrok.io/registerUser`. Note the usage of `registerUser` since that's the endpoint we are handling the mutation in.

### Test the new endpoint

Finally let's try accessing the `/registerUser` endpoint through GraphiQL. We want to make the connection work by returning a dummy value for `id`.

First, we need to make some changes to the handler in `registerUser.rb`. Because we're using `ngrok`, we need to add the following line to the Rails application config:

```
config.hosts = nil
```

It will then look like this:

```
class App < Rails::Application
  routes.append do
    post "/registerUser" => "hasura#register_user_handler"
  end
  config.hosts = nil
  config.consider_all_requests_local = true # display errors
end
```

Also, make the handler endpoint return a dummy value for `id`:

```
class HasuraController < ActionController::API
  def register_user_handler
    request_data = params[:input]
    puts request_data
    render json: {id: 1}
  end
end
```

After that, restart the Rails server.

If we now go back to GraphiQL on the Hasura console, and run the mutation again, the dummy `id` will be returned:

![](https://hasura.io/blog/content/images/2020/08/image-3.png)<figcaption>registerUser mutation served by Ruby</figcaption>

Alright! We have a working GraphQL API that is resolved using a Ruby REST API written using Rails.

Now you can modify your handler code as required to do any business logic; like connecting to a different API, connecting to a database (preferably using Hasura's APIs) or using an ORM for a different databases etc. Hasura will take care of proxying the GraphQL mutation to the right REST API handler internally.

### Connecting to database from inside Rails

You can connect to the database and perform any operations. The easier way to perform reads and writes to the database is to use the auto-generated GraphQL API of Hasura inside the handler. By making use of an [admin secret](https://hasura.io/docs/1.0/graphql/manual/deployment/securing-graphql-endpoint.html) you can perform any query from the backend as you will be simulating an admin permission with no restrictions.

## Permissions & relationships

The access control logic for this GraphQL API can be declaratively configured using Hasura's role based permission system.

Head over to the `Permissions` tab on the action page to add a role and allow access.

![](https://hasura.io/blog/content/images/2020/07/Screenshot-2020-07-14-at-4.46.37-PM.png)

In the above example, we created a role called `public` that is allowed to execute this mutation. To read more about how authentication & authorization works with Hasura, you can check out [the docs](https://hasura.io/docs/1.0/graphql/manual/auth/index.html).

This permission can be applied to both custom queries and mutations. Let's look at how this will apply for a relationship. Now consider that the Postgres database has a `users` table. We can connect the `id` of the `registerUser` output to the `id` of the users table.

![](https://hasura.io/blog/content/images/2020/07/Screenshot-2020-07-14-at-4.54.02-PM.png)<figcaption>Relationship from action to postgres</figcaption>

The related data also conforms to the permissions defined for the respective table (i.e. users).

## Existing REST API

So far, what we have been looking at is creating a GraphQL API from scratch by mapping it to a new REST endpoint. What if there is an existing REST API endpoint that you want to reuse as a GraphQL API?

It's quite possible to do this, as long as you are able to handle the request body format that Hasura sends to the endpoint. The format looks something like this:

```
{
  "action": {
    "name": "<action-name>"
  },
  "input": {
    "arg1": "<value>",
    "arg2": "<value>"
  },
  "session_variables": {
    "x-hasura-user-id": "<session-user-id>",
    "x-hasura-role": "<session-user-role>"
  }
}
```

In our example, the input arguments - name, email and password - were wrapped inside an `input` object. Now if you can make necessary modifications to your existing REST API to handle this request body, your GraphQL API will work as expected :)

## Deploying a Ruby API

The Ruby API can be deployed as a serverless endpoint or lambda function if you want to architect your application in the [3factor](https://3factor.app) architecture. It really doesn't matter where you deploy it since at the end of the day, Hasura just needs an HTTP POST endpoint for each action handler.

This way, you can host a bunch of endpoints in the same server or hand off to serverless functions for each modular functionality. The GraphQL type system and metadata lives at the Hasura layer and hence your serverless function or endpoint doesn't need to worry about that part of the logic.