Harry Potter and the Javascript Fatigue — Part 3
This is the last part (for now) of a series of articles on Vue.JS frontend development. In case you missed them, here is Part 1: The Philosopher’s UI
and Part 2: The Chamber of Storage
You can pull the App from here! If you do so, run git checkout part3
for this part, the final App is git checkout final.
Part 3: The Prisoner of Api
You can create and change Todos but one major draw-back is that you lose all state after restarting. You feel that having to apply a reappeariamos spell is a huge time-waster. So you ponder different solutions. Your idea is to banish the Todos to a place like Azkaban. To imprison them frozen in time in a basement of data, a database ment. You find such basements in the internet and after some consideration you decide to give either Cassandra or MongoDB a try. But you would never use Voldemort. Never.
The deus-ex-machina-backend
Because a wizard does not submit himself to backend work, you ask your house elf to write some backend for you. And he quickly comes up with a Reactive Repository
The little creature defines an Id on each task.
The elf tells you something about Command Response Segregation and how Hexagonal Architectures is the greatest discovery since coffee mugs.
He talks and talks. Now he mentions reactive backends and how reality is nothing more than a stream of past events in a message queue. You start to pity him and his simple elf mind
He finally starts to make sense when he talks about the Rest Api. He will define a GET
and a POST
endpoint to fetch all tasks or post a new one.
Asynchrounus testing
So the only thing left to do is writing an Api that calls the backend and updates the state of the store. That should not be too hard to do. First you have to install a library to make calling the backend a bit easier.
You let yarn do the magic: yarn install axios
. And now you describe the api. You want it to fetch tasks und unwrap them from the more technical Axios Response. Also if an error occurs you must hand it down to the caller.
These two tests also use Jest’s async testing tools and mock away the axios.get
method using Jest! (Note they are not annotated as async, but return a promise!)
The api itself is pretty easy to build now.
Now that the Api is ready, it makes sense to write tests for the Actions. Actions do not change the state, they only invoke mutations, as you saw in the previous Part. So this is what you define here.
- If the Api succeeds, it calls a FetchTasksSucceeded Mutation
- If the Api fails, it calls a FetchTasksFailed Mutation
You need to mock the fetchTasks
method of the Api and have it return Promises. After looking at the Vuex documentation, you find out that Actions can return Promises. So you can even await the action from your testing code!
Compared to the version that returns a promise this is more succinct. You can await any promise and then assert things.
Implementing the Action is again pretty much straight-forward.
This was — to your surprise — less painful than the Herbology courses at Hogwarts. But only a little bit.
Posting data
Fetching an empty list from a database ment is not of much use. You have to fill it somehow. So you will use the POST
endpoint the elf defined and write your api function.
This is exactly the same as for your fetching endpoint. But you did not specify that the way data gets to axios
. Adding an extra expect statement might make sense
Here you could also specify that the correct request headers are set. The downside about these tests is that they depend on the library used. But this goes for all tests against the backend. In a perfect world, we would create a mocked endpoint using something like Wiremock. Unfortunately, I do not know a reliable method to use such tests in the frontend (yet).
You enhance the AddTask
action by calling the api.
The action and your test became asynchronous but not much changed besides that. The Action itself is nothing more than an api call.
You do not have to create a Task
object anymore, because you get one from the Api and it already contains an ID.
A type casting Api
Wait it contains what?
The task object you receive from the Backend contains an Id field. (You vaguely remember the house elf mentioned that). But your task model in the frontend does not have such a thing. You realise that all the type-safety in your backend calling expressions like
is nothing more than a compile-time illusion. You do not like illusions. True wizards do not deal in illusions, only in like actual real magic. Talking to a backend that might give you any object it wants to. Reality is outrageous! You have to do something about it (And now you can appreciate that you decided to put the api in its own layer)!
What you want is something like this:
You remove all the unknown fields from the result and each object must have the right types in the api. That is the wizard’s way.
First you add the id to the Task
and then you put another function into the api that casts your objects onto the right type.
You do not pretend anymore that you already get the correct type from the backend, you map to your own type.
Casting Refactoriamos
All these Promises with their catch-then-resolve-reject semantic are very verbose and hard to read. You learned the Refactoriamos spell at Hogwarts and thought you could try it out on your api code.
This is all that remained after the spell evaporated many of your lines. And all tests are still running green! What a great spell indeed. By making all functions asynchronous, it removed the helper function completely. Your code is no longer in the complicated swamp of Promise handling.
You cast the spell again, this time on your AddTask action and you get:
On first glance, the spell did not do much. But you can see that it got rid of the chain of then
s and the catch
function somewhere. You have a nice try catch
block instead. You apply the spell to your other actions in the same way. And feel more confident with your codebase.
You also cast the spell on some of your tests, they stop being so hideous.
Again instead of creating Promises you just await a result and use it.
There is also vuex-saga based on redux-saga that takes these ideas even further. But, by the time of writing vuex-saga has a bug that seems to make it harder to handle errors. Also sagas might not be necessary for every team.
That’s it for now. You feel relieved. Finally, you can think about the important topics of web development. Like what color you want for important todos or how many pixels of border radius are necessary for your Textboxes to impress Ginny Weasley.
/