Build a Post activity log using React & AWS Amplify
This is a series of posts where I document my learnings & findings while building two webapps for two family businesses
TL;DR.: checkout this example repo on how to achieve this using AWS Amplify & React. Any feedback is welcome!
At Hamerlin, as in any other company, data is crucial. having a goto place to find all the information about clients & reports is essential to the business. Not only that, but knowing who and when update, create and delete data.
We've seen this in many of the services we use in our daily lives, eg: Trello or Google Drive. Because is something usually users expects, I naively thought that it was something ready to plugin in place and start using it, but it doesn't. In fact, It's more complex that you might think. To illustrate a real world example, let's take a look at how Trello approach this:
How Trello stores User's activity data
they call them Actions, and as in there website, here's their definition:
Actions are generated whenever an action occurs in Trello. For instance, when a user deletes a card, a deleteCard action is generated and includes information about the deleted card, the list the card was in, the board the card was on, the user that deleted the card, and the idObject of the action.
Trello Actions in the activity feed in the board sidebar
Trello Actions in a card's activity feed
Basically they not only store all the changes that happened, but they identify changes by type and store it accordingly, including a bunch of data related to the modified resource. This is important for them because they not only list the generic changes, but they also include links to the resource, related data like the board was in, the column. the user, etc.
To be honest, this approach is amazing but is TOO complex to what (I think?) we can achieve in just a couple of hours. I want to build this app incrementally, so I will present you my fisrt approach and then we can talk about the ideal escenario.
Let's build our Activity log
First of all, make sure you have an Amplify + React project setup ready to start. you can use whatever entity you want, in this cas I will use a Post type for the sake of simplicity.
Let's define our schema
As I said before, this is the simplest approach to get the job done. it may be better ways to do this, but we are Lean developers and we can improve this in another iteration :)
The goal is to store the history of changes of each post.
the fields I want to store per-change are:
- the post ID
- the author of the change
- a timestamp of the change
- the action type
- and the resulted post after the change
amplify add api, we can edit our
schema.graphql. It should look something like this:
Then we are ready to run
amplify push and start testing our implementation. But first let me explain my approach here:
The idea is that everytime a Post is being either
DELETED, I also create a new
PostHistory that stores data from the previews operation. The cool thing is that because we are using GraphQL, we know before hand which operation the user wants to execute, so it's easier to populate the
PostAction enum from it in our code. Also we have all the possible queries & mutations generated by the Amplify CLI 😉
Another thing to point here is the
HistoryPayload type. I use a simple type here instead of using the special
@connection directive from AWS Amplify, because I'm interested in storing the exact post's data at that moment when the operation happened. Using the @connection will store an actual reference to the Post, and that's not the goal of that payload.
Create a post using React
First we need to create our
CreatePost component with a simple form. Let's focus on the
As you can see, I'm creating a post as you normally will do with the Amplify's
API module. After the creation, I take the result and create a new
PostHistory using the generated
createPostHistory mutation. You should see something like this:
You can also checkout how the UpdatePost component works, it's pretty similar to the creation.
Of course this is a really simplistic example, and it has a couple of limitations that I should consider for future iterations:
- Currently the
creatorattribute is a string, but eventually I would like this to be a connection to the actual user that's logged in for each operation, this will let me not only get access to more data from the user, but also flexible for changes on the user's data.
- Now I now this is a more
RESTapproach, we are making 2 round trips to the server for one single user's operation. In the future, I would like this to be automatic, and maybe call a Lambda function from any DynamoDB event? or maybe a pipeline resolver could work?
- if you see the
Post.jscomponent, you see that I create aldo 2 queries to get the data for the frontend. This is OK, but maybe I would like to change my schema to something like this:
I think having the
actions inside the Post type it's more ellegant. I don't know how difficult this may be, but it looks cool right? This may be possible using the new @function directive, I can execute a Lambda function and access the dynamoDB database directly from here. This is someting I will try very soon :)
You can checkout the final result on this repo and let me know any feedback!. I'm learning a lot doing this examples and hope you can take some knowledge from it too!. If you thought any other way to solve this problem or know how to make this example better please let me know!
I would like to thank Kurt & Nader for helping me reviewing this piece of content. They both work as developer advocates at AWS Amplify and they are doing an amazing work helping people like me in the community. kudos!!
I'm going to continue sharing content like this, having your support would be awesome!