Consuming Chuck Norris API from ReactJS
Experimenting with incremental software development practices with ReactJS
There is an infinite amount of resources out there for ReactJS to learn from, but it becomes quite challenging to find quality content which does actually help us growing in the right direction.
One of the things I don’t see very often is how to apply incremental software development practices in a real codebase, so I wanted to take this opportunity to experiment a little bit with ReactJS and Test Driven Development by consuming a remote RESTful API.
I wanted to use a publicly available API for this project, and while exploring the web, my attention was caught by api.chucknorris.io. Yeah that’s right, someone has published a RESTful API that returns jokes about the legendary Chuck Norris.
I believed it had the perfect balance between simplicity and fun, so let’s give it a try!
Let’s set the scene
Your team and the client are having a great time, you all share a profound respect towards the legendary Chuck Norris and you agree it’s time to make the world a better place.
We want to ensure every fan in this world can go into a web page and get amused and reminded about our legendary friend, but how do we proceed?
After a great pairing session with our Product Manager, we decided to start really small, and we come up with a couple of user stories, making sure they are small enough and have all that is necessary for a good story (i.e. INVEST principles).
- As a #1 fan of Chuck Norris, Steve wants to see a joke about his favourite celebrity, so that he can feel amused by Chuck Norris legendary reputation
- As a #1 fan of Chuck Norris, Steve wants to see different jokes about his favourite celebrity, so that he can continue to feel amused by Chuck Norris legendary reputation
These two stories are enough to start with, and we can certainly add more stories down the line, for instance looking after on demand capabilities and unhappy paths.
Before we start building anything we need to add some acceptance criteria to our stories, so that we have a rough understanding of what is acceptable for Steve.
Now here is where the N (Negotiable) and the T (Testable) part of the INVEST principles come very handy, especially to help us narrow down what we want to achieve and know when we’re done.
We paired with our Product Manager and we agreed with the following acceptance criteria for the first story.
Given Steve visits the home page
When the page has finished loading
Then Steve should be presented with the following joke
“Chuck Norris doesn’t read books. He stares them down until he gets the information he wants.”
We negotiated the story so to cover only the very first happy path, where we do show only one carefully selected joke about our legend, this will allow us to start small and focus on getting things done.
Ok we now have enough information to start building something for Steve.
First user story
First thing first, as you can imagine from the title of this article, we are going to be using ReactJS, together with TypeScript, to build our app.
We also mentioned that we are going to integrate with a third-party public API offered by api.chucknorris.io, so we don’t need to invest any time building a dedicated backend for this purpose.
That being said I hope that at this point you already figure out that we don’t actually need to build any integration yet for the first story, yeah that’s right, we can simply hardcode the very first joke and fast track directly into the second story, where we are going to have some action!
Great, let’s start by creating a new react application using the create-react-app
command line tool.
Let’s get rid of everything in the App
component until we are left just with this.
What’s the plan? Ideally we want to take a look at the functional part first, then we will take care of styling, and once we are done with that we can move onto the second story.
So we talk to our Product Designer and we agreed on having some sort of dialog or speech bubble, think Gameboy-like, which is going to display the joke with a typewriter effect.
This gives us a rough idea on how things will be laid out on screen, we can then start by creating a new component, namely ChuckNorrisDialog
and prepare a test for it as well.
Our job here is quite simple, we just want to make sure that, once the component is mounted, we display the given joke on the screen, so we can write a simple failing test (remember we are practicing TDD), which is going to look like this:
How do we make it pass? Well as mentioned earlier this story just is simple enough for us to go through a very first setup, so let’s make the simplest possible implementation for it and move on.
We have enough functionally to help us satisfying our first story, we just have to load this component in the App
and work a little bit on the styling.
Let’s do start the development server to see how it looks like
Ok, it works as expected but it looks sadly empty.
Jokes with style
We got some serious ideas from our Product Designer, let’s go do some CSS magic and give it a little revamp. (I am going to provide the CSS code as part of the source code linked to at the end of this article).
We are going to rename App.css
into App.module.css
to hold our basic styling, and then add a ChuckNorrisDialog.module.css
as well. If you are not familiar with CSS modules in React you can read about them here.
We are also going to install a new font, namely the pokemon-font
, available here, and import its css directly into our App
component.
In order to install pokemon-font
we can simply run yarn add pokemon-font
.
And for the final touch we decided to add an avatar on the page, I found this one that looks pretty good, and we can simply put it within an asset folder and load it directly in the code like this:
And voila, much better!
I believe we are done with the first story, let’s commit the changes and move to the second one.
Second user story
We haven’t defined any acceptance criteria for the second story yet, so let’s do that first.
Given Steve visits the home page
And there is already a joke displayed
When Steve refreshes the page
And the page has finished loading
Then Steve should be presented with a different joke every time
Now, we can argue that we can actually implement this story in many ways, we could for instance have a static file locally populated with loads of jokes taken from the internet, but the main aim of this exercise is to consume a real RESTful API so that’s what we are going to do.
The Chuck Norris API supports a very simple operation that return a random joke, and the structure of the request and response seems pretty straightforward.
Here’s a sample request:
And this is the payload in the response:
What’s the plan here? Ideally our ChuckNorrisDialog
component will leverage some other Client
component, which in turn will be responsible for the API integration. Then we are going to use the context hook in combination with a context provider in order to supply the client to any part of the app. If you are not familiar with React context you can read about them here.
Let’s start by creating a simple interface for the API, a basic client test and implementation.
I read many articles and seen people testing this type of component by employing conventional mocking techniques, leveraging the fetch
API from one side and then use a fetch
polyfill or stub in the test.
Although this might work, the aim of a test is to give us confidence that the code works as expected, so the closer we get to replicate the production runtime the more confidence we gain.
Luckily for us there is a better approach, we can leverage the Mock Service Worker, which can intercept real http requests and provide a way to prepare canned responses declaratively.
Let’s add it via yarn
and prepare our first failing test.
In order to make it pass we can simply use fetch
and cast the response as a Joke
.
Cool, we now have a functional and simple API client implementation.
The next step would be to implement a simple context provider that we can use in order to inject the client wherever we need it.
What’s next? It’s time to implement the changes into our ChuckNorrisDialog
component, by writing more tests first, obviously!
In order to get extra support for writing our test cases, we will make use of an additional jest extension, particularly helpful when mocking interfaces.
We can add then by running
Here’s our updated test:
Because we are introducing asynchronous side-effects, using the useEffect
hook and promises, our tests needed a little bit of extra attention with respect to rendering, failing to do so increases the risk of bringing up the (in)famous error:
We can avoid that by making sure operations that lead to rendering are wrapped against, what I call, a fully fledged act
. That looks like this:
The important bit of this fully fledged act
is the callback, because it’s async, and apparently that ensures all the pending promises are resolved before continuing. If you want to know more, here is a better explanation of how act works.
Here the updated implementation for the ChuckNorrisDialog
Great we just have to go inside our index.tsx
and make sure it’s setup correctly, once this is updated we can commit our changes and we should be done with this story as well.
Whenever we refresh the page we get a new joke, which is exactly what we wanted!
What’s next?
When working incrementally, we have the ability to work on different dimensions, and decide what we want to expand on.
For instance we started by looking at how to provide just one single joke, hardcoded, then we moved into presenting multiple different jokes.
But there are other things we might want to consider next:
We could think about adding on-demand capabilities, i.e. improving user experience by providing Steve a “Next” button to get another joke instead of relying on a page refresh.
Maybe think about unhappy paths, i.e. what happens if, for some reason, the Chuck Norris API temporarily stops to respond, should we present something else to Steve? Should Steve be able to retry later?
What about narrowing down the random jokes within specific categories, supported by Chuck Norris API? The possibilities are endless.
Conclusion
We just scratched the surface here, but I hope you enjoyed our little adventure.
The source code used for this article is available here (bonus: this code actually has few more commits which covers the on-demand and error handling stories).
I’ll see you at the next one!