Developing Geonotes — Creating new notes — Ep. 4

Developing Geonotes — Creating new notes — Ep. 4

I'm finally adding some interactivity to the map. Here's how I did it.

Now that we can display notes in a pretty way (read more in Episode 3), 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.

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.

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

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

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

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

🚧 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