Developing Geonotes — Creating new notes — Ep. 4
Tags: react, javascript, typescript, coding
Published: 2021-08-06 19:56:47

Now that we can display notes in a pretty way (read more in [Episode 3](https://dev.to/emilioschepis/developing-geonotes-animations-and-interactions-ep-3-1f9)), it is time to let the users create their own notes.

This will mainly be a technical post, since much of the changes are business logic related.

## 🔧 Custom business logic with Actions

As I anticipated in the last episode, I decided to only let users create notes by invoking a  [Hasura Action](https://hasura.io/docs/latest/graphql/core/actions/index.html).

What this does, is allow you to have a GraphQL interface (like you would have for database queries and mutations) to a serverless function.

In Geonotes' case, the actions will call a [Firebase Cloud Function](https://firebase.google.com/products/functions).

The first step is to define the action's input and output in the Hasura console.

![Screen Shot 2021-08-04 at 08.49.16.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628059782931/624nlFm3X.png)

You can then define which endpoint is responsible for handling the action.

## 🔥 Handling the action with a Cloud Function

The action sends a `POST` request to the specified endpoint, so the function can extract session variables and the various parameters passed as input.

To continue the type-safety first approach I created a small helper that, before running any code, verifies the authenticity of the request and that the parameters are valid. [Source](https://github.com/emilioschepis/geonotes-backend/blob/02fe4bc579fee54909d7d56e7930562e74db0524/functions/src/utils/actionsHelpers.ts#L12-L47)

```typescript
export const actionWrapper =
  <I, O>(handler: ActionHandler<I, O>) =>
  async (req: functions.https.Request, res: functions.Response<O | ErrorOutput>): Promise<void> => {
    // ...

    const variables = req.body.session_variables;
    const userId = variables["x-hasura-user-id"] as string;

    if (!userId) {
      res.status(400).send({
        code: "user-required",
        message: 'Actions should be performed by a specific user. No "x-hasura-user-id" was provided.',
      });
      return;
    }

    const sendOutput = (output: O) => {
      res.status(200).send(output);
      return;
    };

    const sendError = (error: ErrorOutput) => {
      res.status(error.status ?? 400).send(error);
      return;
    };

    return handler(userId, req.body.input, sendOutput, sendError);
  };
```

Using this wrapper, actions now have a much simpler interface for sending the responses or errors in the supported format. [Source](https://github.com/emilioschepis/geonotes-backend/blob/02fe4bc579fee54909d7d56e7930562e74db0524/functions/src/handlers/createNoteActionHandler.ts#L7)

```typescript
const handler: Action = async (userId, params, sendOutput, sendError) => // ...
```

The action is responsible of actually mutating the database with higher permissions, and then returning the id of the just created note.

## ⭐️ The result

I quickly wired up an empty screen with a button to the map screen, so that notes can be created wherever the user is. For now, the text is static as I only needed to test the business logic.

[Watch a short video of a note being created](https://twitter.com/emilioschepis/status/1422816262359498752)

## 🚧 Next steps

As I said in one of the first episodes, notes can only be created by signed in users. Right now, I created a quick account just to have the permissions to invoke the action.

The next step is to add an actual authentication flow to the app, so that different users can create their own notes.

## 🎙 How to follow the project

I'll be posting updates throughout the development process and as I learn new thing regarding development, design, and marketing.

If you'd like to have even more real-time updates you can

- [Follow me on Twitter @emilioschepis](https://twitter.com/emilioschepis)
- Checkout the public GitHub [app repository](https://github.com/emilioschepis/geonotes-app/tree/develop) and [backend repository](https://github.com/emilioschepis/geonotes-backend/tree/develop)