Adding custom business logic to Hasura using Dark
Tags: hasura, darklang
Published: 2020-04-17 06:34:34

---
title: Adding custom business logic to Hasura using Dark
published: true
date: 2020-04-16 09:56:14 UTC
tags: hasura, darklang
canonical_url: https://hasura.io/blog/dark-and-hasura-actions/
---


Hasura has recently implemented a way to create custom mutations called Actions. Want to handle complex business logic? Actions are the way to go!

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-12-at-12.36.49.png)

To create a new action, you need to provide a definition, handler (REST endpoint), and specify the kind – sync or async. When you run the custom GraphQL mutation, Hasura makes a `POST` request to the specified handler with the mutation arguments and the session variables. If you want to know more about the machinery behind it, check out [the docs](https://hasura.io/docs/1.0/graphql/manual/actions/index.html) or our [article introducing actions](https://hasura.io/blog/introducing-actions/).

In this article, we're going to explore how Hasura and [Dark](https://darklang.com/) work together by creating a custom mutation and implementing a handler for it using Dark. And all of that will be done without leaving the browser!

## What is Dark?

Developing services is a pretty complicated job. Before you actually start writing code, you need to decide on stuff like hosting, CI/CD pipeline, language, and then you need to stitch it all together. Another problematic thing around it is deployment. Making a commit, creating PR, running CI, actual deploy — it's a lot. So what Dark aspires to do is to take this whole complexity away from us, and leave developers to only worry about writing code.

As being a _setupless_ solution, Dark consists of language, editor, runtime, and infrastructure, so that you don't need to spend time figuring all of that on your own. What's more, ease of writing _deployless_ backends is one of Dark's primary concepts, so while your writing your code, every change is instantly deployed to the cloud!

The language itself is described as _a statically-typed functional/imperative hybrid, based loosely on ML._ The dark compiler was written in OCaml, and syntax wise, you may spot a resemblance between these two languages.

With Dark, you write your code in a structured editor that makes sure you won't write syntactically incorrect code. There's no parser included, which means no syntax errors — with every keystroke, you modify the AST directly.

![](https://hasura.io/blog/content/images/2020/04/dark-demo3.gif)

## Creating an action

### Online mafia game

Do you know _the_ [_Mafia_](https://en.wikipedia.org/wiki/Mafia_(party_game)) game? It's an old-timey party game in which the objective is for the mafia to _kill off_ civilians until they are the majority, or the civilians to _kill off_ the entire mafia. As the rules can be easily extended, players could be assigned with many different roles.

Yet, for our example, let's take only three: mafioso, civilian, and a doctor. There are also some constraints regarding roles:

- There can be only one doctor.
- The ratio between mafia players and all players should be around 1/3.
- The minimum number of mafia players is 2.

For example, for eight players, there should be one doctor, two mafiosos, and five civilians.

When a new player enters a game, I want to assign him a role. However, how do I make sure the above constraints aren't violated? How do I know what characters are still available?

In real life, someone probably would need to go through all the cards and choose the characters based on the players count, and then deal the cards to the players.

But what about an online version of the game? There is no game master among the players and I can't have information about other players' roles on the frontend, because it's secret. That's when actions come into play! I'm going to create a custom mutation that performs the following logic:

- Fetch all taken roles from the DB, along with the number of players in the game.
- Based on already present characters, check which are still available.
- From a set of available roles, choose a random one.
- Insert a new user into the database.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-15.14.24.png)<figcaption>Hasura custom mutation flow</figcaption>

### New Hasura Action

The first thing to do is to define the mutation and all the required types. The definitions below mean:

- The name of the custom mutation is `CreateNewPlayer`.
- It accepts two arguments: a string `nickname` and `game_id` of type `CreateNewPlayerUuid`, which is a custom scalar.
- The mutation returns `CreateNewPlayerOutput`, which consists of newly created player info.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-15.24.50.png)<figcaption>New Action definition</figcaption>

Next, I need to provide an HTTP endpoint to which Hasura will make POST requests whenever `CreateNewPlayer` mutation is called. In my case, I'm putting a link to my Dark canvass with a route being `new_player`.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-08-at-12.19.42.png)<figcaption>HTTP handler created with Dark</figcaption>

As the last step, I'm saying that the kind of the mutation is `Synchronous`, which means that Hasura would keep the request open till receiving a response from the handler.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-08-at-12.18.50.png)<figcaption>Kind of the Action</figcaption>

## Creating REST endpoint in Dark

If you want to create an HTTP handler, you probably follow these four steps:

1. Agree upon request body parameters.
2. Write an implementation.
3. Make endpoint accessible.
4. Test it by sending a request.

Dark took a different approach. One of the core concepts of Dark is _[Trace-Driven-Development](https://darklang.github.io/docs/trace-driven-development)_. It allows you to develop your backends from incoming requests. In other words, you can send a request to the endpoint that doesn't even exist. Then, based on the received request, you can implement your handler. Let's see it in action!

I'm going to call my newly created mutation.

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

It will result in an error because I haven't implemented it yet in Dark. But I can go to my Dark canvass, check the 404 section and see that Dark captured the request that Hasura for `CreateNewPlayer` action.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-08-at-12.20.26.png)<figcaption>404 section</figcaption>

Now, by clicking `+` button, I can convert nonexistent HTTP endpoint into a real handler. I can also check what exactly was sent to Dark in a request body.

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

### Implementation

I will skip some implementation details related to determining a new role. You can find screenshots with code [here](https://gist.github.com/beerose/c96a082d26551c541eed7e6ba8398e47).

As the first step, I'm making a request to Hasura to extract the information I need – all the roles that are already taken and the number of players. As you can see in the screenshot below, there are already three civilians and one doctor.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.26.07.png)<figcaption>Fetch all needed data from Hasura</figcaption>

The next step is to randomly choose a role from all available roles.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.29.11.png)<figcaption>Available roles</figcaption>

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.30.41.png)<figcaption>New Role</figcaption>

As I have a new role for the player, now I can make a call to Hasura and insert a new player to the database.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.34.21.png)<figcaption>Call to Hasura and handler returning data</figcaption>

All the implementation is done, so now I can start using `CreateNewPlayer` mutation 🎉

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.38.12.png)<figcaption>Call mutation from Hasura Console</figcaption>

## Connecting Action with the graph

The custom mutation I just created returns information about the newly created player. The next thing I'd need is information about the game and other players. But I don't want to make another call to Hasura. Instead, I want to get all the additional info by calling only `CreateNewPlayer` mutation.

In order to obtain that, I'm going to modify the definition, so that it also returns `gameId` and create a new relationship between the Action and `game` table. I also need to return `gameId` from the handler.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-21.51.30.png)<figcaption>Add gameId to type definition</figcaption>

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.53.25.png)<figcaption>New relationship</figcaption>

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-21.50.22.png)<figcaption>Return gameId from the handler</figcaption>

Thanks to the new relationship, I can now fetch all the data about the game I want! I can fetch info about the players associated with the same game as well.

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.53.59.png)<figcaption>Mutation</figcaption>

![](https://hasura.io/blog/content/images/2020/04/Screenshot-2020-04-11-at-17.49.43.png)<figcaption>Result</figcaption>

## Summary

We explored how to add custom business logic to Hasura in a few steps, learned how to take advantage of the Trace Driven Development, and how quickly we can get a rest endpoint up and running with Dark. Moreover, we added a custom mutation with Hasura Actions, and took things to the next level by creating an relationship and connecting it to the graph!

Enjoyed this article? Join us on [Discord](discord.gg/hasura) for more discussions on Hasura & GraphQL! 

Sign up for our [newsletter](http://eepurl.com/dBUfJ5) to know when we publish new articles.