Turn your Python REST API to GraphQL using Hasura Actions
Tags: python, graphql, restapi, hasura
Published: 2020-08-17 04:39:30

---
title: Turn your Python REST API to GraphQL using Hasura Actions
published: true
date: 2020-08-14 08:59:30 UTC
tags: python, graphql, restapi, hasura
canonical_url: https://hasura.io/blog/turn-your-python-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 just defining types and configuring endpoints._

![](https://hasura.io/blog/content/images/2020/08/python-rest-api-graphql.png)

In this post, we will look at using [Hasura Actions](https://hasura.io/docs/1.0/graphql/manual/actions/index.html) to convert your Python REST API to GraphQL.

Typically, setting up a GraphQL server from scratch would involve writing the GraphQL schema, type definitions and wiring it up with the `resolvers`. With Hasura Actions, the complexity of writing resolvers is reduced as one can just write a REST API POST endpoint and use that as a resolver for the GraphQL query or mutation.

[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 realtime subscription use cases. But when there's some business logic that needs to be written, we can make use of Hasura Actions to map a REST API to be a custom resolver. We will look at how to do this with Python in this tutorial.

## Create a Hasura Cloud Project

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

<!--kg-card-begin: html-->[![](https://camo.githubusercontent.com/a6de317cd7d0ed4e8722684b428f72e3da614fe8/68747470733a2f2f6772617068716c2d656e67696e652d63646e2e6861737572612e696f2f696d672f6465706c6f795f746f5f6861737572612e706e67)](https://cloud.hasura.io)<!--kg-card-end: html-->

Hasura requires a Postgres database to start with. We can make use of Heroku's free Postgres database tier to try 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 option to 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 you are done creating the project, click on `Launch the Console` button on the [Projects](https://cloud.hasura.io/projects) page 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

[Actions](https://hasura.io/docs/1.0/graphql/manual/actions/index.html) 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.

Now let's head to the `Actions` tab on the Hasura Console and define the GraphQL types for our app.

![](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!
}
```

Here we are defining the Mutation type `registerUser` which accepts the `name`, `email` and `password` arguments and returns the `id` of the user.

We can configure the handler URL later. Click on `Create` 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: "Praveen", email: "praveen@hasura.io", password: "mysecretpassword") {
    id
  }
}
```

The above mutation should obviously give a `http` exception since the Handler URL has not been configured yet. But this test is to verify that Hasura generates mutations for you based on the types defined to query on the same GraphQL endpoint.

![](https://hasura.io/blog/content/images/2020/08/try-python-mutation.png)

Now let's go ahead and actually define the Python app and try out the mutation.

### Codegen: Auto generate boilerplate code

Now head over to the Codegen tab to autogenerate the boilerplate code for your python flask 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/codegen-python-flask.png)

We are using the python framework `Flask` for this example, there's also python-fast-api boilerplate that can be used to replicate a similar example. In fact, any python server that can serve a POST endpoint can be used to swap with this example.

### Business logic required:

- 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.

The generated codegen (registerUser.py and registerUserTypes.py) can be copied to your filesystem to start making the changes to the handler.

```
from registerUserTypes import registerUserArgs, UserOutput
from flask import Flask, request, jsonify

app = Flask( __name__ )

@app.route('/registerUser', methods=['POST'])
def registerUserHandler():
  args = registerUserArgs.from_request(request.get_json())
  print(args)
  # business logic here
  return UserOutput(id="1").to_json()

if __name__ == ' __main__':
  app.run(debug = True, host = '0.0.0.0')
```
<figcaption>registerUser.py</figcaption>

```
from dataclasses import dataclass, asdict
from typing import List, Optional
from enum import Enum, auto
import json

@dataclass
class RequestMixin:
    @classmethod
    def from_request(cls, request):
        values = request.get("input")
        return cls(**values)

    def to_json(self):
        return json.dumps(asdict(self))

@dataclass
class UserOutput(RequestMixin):
  id: any

@dataclass
class Mutation(RequestMixin):
  registerUser: Optional[UserOutput]

@dataclass
class registerUserArgs(RequestMixin):
  name: str
  email: str
  password: str
```
<figcaption>registerUserTypes.py</figcaption>

Once you copy the codegen, you can setup flask and start running the server:

### Setup Flask

You can start by installing flask:

```
pip3 install flask
```

and then run the python server using:

```
python3 registerUser.py
```

With this you should start seeing your flask server running on port 5000.

![](https://hasura.io/blog/content/images/2020/08/flask-dev-server.png)

With this, we need to update our handler URL for the Action so that the HTTP call works. Add the `registerUser` endpoint that handles the mutation. So the final handler URL will look like `http://localhost:5000/registerUser`.

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

Once you have ngrok installed, you can run the following command:

```
ngrok http 5000
```

This should expose your local python server running on port 5000 on a public URL (something like `http://b4318818ea61.ngrok.io`).

Go back to the Modify tab of the `registerUser` Action that was created 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.

Finally let's try out the same mutation through GraphiQL and the connection should work returning a dummy <value> for `id`.

![](https://hasura.io/blog/content/images/2020/07/Screenshot-2020-07-14-at-4.42.06-PM.png)<figcaption>registerUser Mutation served via Python</figcaption>

Alright! We have a working GraphQL API that is resolved using a Python REST API written using Flask.

Now you can modify your handler code as required to do any business logic; like connecting to a different API, connecting to a database (preferrably 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 flask

You can connect to the database and perform any operations. The easier way to perform read and writes to the database is to use the auto-generated GraphQL API of Hasura inside the handler. By making use of `Admin Secret` 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, I have created a role called `public` that is allowed to make this mutation. To read more about how the whole [Authentication and Authorization](https://hasura.io/docs/1.0/graphql/manual/auth/index.html) works with Hasura, you can checkout the docs.

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 `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. (ie. users)

## Existing REST API

So far, what we have been looking at is create 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>"
  }
}
```

So in our example, the input arguments - name, email and password was 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 :)

## Supported Frameworks

This was an example with Python Flask. But as you can imagine, this would work with any python server framework that can serve a POST Endpoint. So you can technically use frameworks like Django, Bottle, gunicorn, Pyramid, Tornado, web2py and so on.

## Deploying Python API

The python API can be deployed as a serverless endpoint, lambda function if you want to architect your application in the [3factor](https://3factor.app) architecture. It really doesn't matter where you deploy since at the end of the day, Hasura just needs a 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.