Turn your Node.js REST API to GraphQL using Hasura Actions
In this post, we will look at using Hasura Actions to convert your Node.js REST API to GraphQL.
Typically writing a GraphQL server from scratch will involve setting up the GraphQL schema, type definitions and wiring it up with the resolvers
in the language/framework of choice. The hard parts about this server component is the resolver code for someone who is used to writing a REST API with GET
or POST
and handling auth logic
for each type; which becomes complex as the app grows bigger.
Hasura reduces bulk of this complexity of setting up a GraphQL API from scratch by giving you instant GraphQL CRUD for databases (currently Postgres) which should cover most of the data fetching and realtime subscription use cases.
In case of custom business logic, Hasura lets you extend the Graph through different ways. If you are comfortable writing GraphQL servers, you can totally add a custom GraphQL server as a Remote Schema
and Hasura merges it automatically for you. In case you are looking to write a new or keep your existing REST API for the custom logic, you can do so using Actions
which is what we will focus in this post.
Actions
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.
An action can be either a query or a mutation. We will look at how to implement both.
New REST API
In case you are starting from scratch, we will define the GraphQL types required for the Action and create a simple Express app to handle the resolver bit. Let's take the example of registering a user.
Before we begin, let's create a project on Hasura Cloud to setup the Action. Click on the Deploy to Hasura button below, signup for free and create a new project.
Hasura requires a Postgres database to start with. We can make use of Heroku's free Postgres database tier to try this app.
After signing in with Heroku, you should see the option to Create project.
Once you are done creating the project, click on Launch the Console
button on the 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.
Now let's head to the Actions
tab on the Hasura Console and define the GraphQL types for our app.
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.
Now let's verify the generated GraphQL mutation by trying out the sample mutation in GraphiQL.
mutation {
registerUser(name: "Praveen", email: "[email protected]", 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.
Now let's go ahead and actually define the Node.js 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 nodejs-express server. Since we are generating the API from scratch, we need the full server setup instead of just the POST
handler.
The console gives you options to generate code for different frameworks including but not limited to nodejs-express
, nodejs-zeit
, nodejs-azure-function
etc.
Let's quickly deploy the nodejs-express app to Glitch so that we can add this request handler to the server. Click on Try on Glitch
to remix an express app on Glitch.
Once you are done, add the generated action handler code from the Codegen tab to the server.js
file. Finally we need to update our handler URL for the Action so that the HTTP call works.
Capture the Glitch URL which will look something like this https://stealth-puddle-cowl.glitch.me/
and add the registerUser
endpoint that handles the mutation. So the final handler URL will look like https://stealth-puddle-cowl.glitch.me/registerUser
.
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.
Finally let's try out the same mutation through GraphiQL and the connection should work returning a dummy <value> for id
.
Alright! We have a working GraphQL API that is resolved using a Node.js REST API in the background.
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.
Permissions
What about permissions? Who will be able to make this GraphQL mutation from the client? Right now, it is configured to be admin only. We can configure roles to allow other kind of users to perform this mutation.
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 works with Hasura, you can checkout the docs.
Relationship Data
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.
The related data also conforms to the permissions defined for the respective table. (ie. users)
Existing REST API
Now you might be wondering what if I already have a Node.js server with a bunch of endpoints written to handle some custom business logic. In that case, as long as it's a POST
endpoint, you can just define the necessary GraphQL types and make some modifications to the way you handle request body to get it working quickly.
The request payload is of the format:
{
"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 :)
Query Action
The above example showcased how to perform a GraphQL mutation. The same workflow can be extended to perform a GraphQL query which proxies to a Node.js REST API in the background. Typical use cases for query actions include computed fields, data enrichment, data transformations, fetching from multiple databases and API sources etc.
Supported Frameworks
The REST API can be in any framework like Express.js, Next.js or serverless functions. After all, Hasura just requires a reachable POST endpoint that it can forward the query to. In case you are going to deploy the API to a serverless function, you will need to follow the format of the cloud provider.
For example, Next.js has API routes which can be deployed as serverless functions and each API route follows a format. Here's a quick example following the Next.js function format and can be deployed to Vercel.
Examples
Head over to hasura-actions-examples for more practical real world use cases like validations, payments, emails etc where a Node.js REST API was used to convert to GraphQL using Actions.
We have a Youtube Playlist with a bunch of examples going over writing out a REST API in Node.js to be used via Actions. You can also check them out.
FAQ
- How do I protect my action endpoint from being called directly?
Though the Action Endpoint is not visible to the public, it is a good practice to use a secret that can be forwarded from Hasura through headers to identify if Hasura is making the request. If not, you can reject the request with a 401 unauthorized.
- What's the best way to deploy my REST API?
Your Node.js API can be deployed on any cloud provider. If it's an express.js app, you can write multiple action endpoints using the same app and deploy one server or compose them individually into serverless functions which can be deployed on Vercel / AWS Lambda and so on. Hasura just needs a HTTP POST endpoint for each Action handler.
- Can I connect to the database inside my Node.js handler?
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.
- Can I use a CLI to generate the action boilerplate code and not use the UI?
Of course! You can make use of the Hasura CLI to generate an action handler that clones the boilerplate code from the framework you choose, all from your commandline inside your Hasura project.
Additional Reading:
Check out the Node.js backend course. In this course, you will learn how to integrate Node.js in a GraphQL backend server stack with Hasura.