Leveling Up: From Create-React-App to Express (& some neat styling libraries)

April 10, 2018 0 Comments

Leveling Up: From Create-React-App to Express (& some neat styling libraries)

 

 


Two months later - time for my second article! You can read my previous article on Visualizing Fibonacci here.

Website Screenshot

Link to the Live Site

The Project

I'm a professional software engineer and still to this day I find that saying this out loud scares me a little. To try and concrete my confidence in my skills and abilities I've been engaging in a few personal side-projects where I take the fundamental methods of building an application and apply them to some sort of fun side project. This is the first in a hopeful series of posts about my experiences to date.

I had previously developed a few create-react-app static site generation projects that I hosted via GitHub pages as a simple way of developing my understanding of the React component library. For this project I decided to take the create-react-app boilerplate, tie it into the Express web framework, and host it online somewhere for free (copying a GitHub page type flow) so I can easily have it linked on my GitHub repository for anybody wishing to view my personal portfolio (for the total price of... free!).

I wanted an easy way to communicate with some public API but rather than expose the API I was communicating to on my client side code I wanted to keep it on the server side to simulate communicating to my own APIs running on confidential endpoints. I had found a couple of Cat APIs that returned some random images so I figured I'd roll with those and see what I could come up with. You can check the APIs out here:

The end application simply displays an image of a cat that changes on click/tap by making a call to our backend express server, which calls the cat api, and returns the image url for display on the main web page. I added in an additional Spotify player of some random cat noises as a requested extra feature. You can check out the live version of the site here.

The Initial Setup

I used create-react-app as a starting point for my project. It's a very friendly boilerplate that'll get you setup with everything you need to start making a basic React application. In a more professional environment it has certain drawbacks related to the customization of some of its webpack configuration but for the purposes of this project its more than sufficient. Once installed it's as simple as running three commands to get your app started:

create-react-app my-app 
cd my-app
npm start

Awesome so now we have our application running!

To handle some of the styling aspects of the application I installed two additional packages:

I highly recommend checking out both libraries if you are new to React and designing some basic UI frontend work. Heres a little summary of what the main purpose of these libraries are:

Styled Components

The common problem with styling, at least during my own time working in web development, has been thatthe global nature of css app styling (at least in the create-react-app framework) makes it difficult to customize your CSS on a component by component level.

The Styled Components library lets you rely on component-defined styles. An example of this from my own code looks like this:

import styled from "styled-components"; const CatPic = styled.imgmargin: auto; background-color: white; max-width: 60vw; max-height: 60vh; margin: 2em; border-radius: 10px; &:hover { cursor: pointer; } border: 0.2em solid lightgrey; 
;

When using it in my React Component instead of using an <img/> tag I just have to use a <CatPic/> tag and it will import the custom styling defined above.

Grid Styled

Grid styled offered me an easy solution to allow my main Cat component to take up 3/4s of the screen at full resolution, and my Spotify component to take up 1/4 of the screen. Then when the resolution changed to a phone styled size it would flip and allow a full width for the Cat component and a full width for the spotify component. This is a godsend for easy mobile responsiveness design. No more auto calculating pixel widths!

A simplified version of this layout can be seen below:

<Flex flexWrap="wrap"> <Box p={3} width={[1, 3 / 4]}> ... cat component here </Box>
 <Box p={3} width={[1, 1 / 4]}> ... spotify component here </Box> 
</Flex>

Integrating Express

Now onto the more challenging part. After installing Express (npm install express) I created a server.js file in the main project directory which looks like the below

const express = require("express"); 
const bodyParser = require("body-parser");
const path = require("path"); const app = express(); app.use(express.static(path.join(dirname, "build"))); app.get("/", function(req, res) { res.sendFile(path.join(dirname, "build", "index.html"));
}); app.listen(process.env.PORT || 8080);

After setting up this file I had to modify some of the scripts in the package.json file

"scripts": { "dev": "react-scripts start", "start": "node server.js", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } 

These new scripts mean that when the dev runs the command npm start rather than running the standard react-scripts start command it will instead run node server.js. The main thing from the script above is in the app.get("/") section which essentially means that when the dev goes to port 8080 after running npm run start Express will direct the individual to the index.html file of the main react application.

The important thing to note at this point is that in order to have valid index.html created you need to have run npm run build prior to npm run start. Rather than having to wait to run a new build and start it every time you make any local changes, I added an extra script called 'dev' which runs the application directly on port 3000. This is a nice work around for developing on the applications features without caring about any of the other server issues.

Another required change I made to the package.json was the inclusion of "proxy": "http://localhost:8080" which proxies the Express server to port 8080, so when the service is running in production any other endpoints setup (i.e. app.get("any-other-endpoint-here") will be accessible when the app is exposed at a URL online.

Frontend to Backend Communication

I added an additional endpoint to the server.js file:

app.get("/cat", function(req, res) { axios .get("http://thecatapi.com/api/images/get") .then(data => res.send(data.request.res.responseUrl)) .catch(err => console.log(err)); 
});

I had setup a Cat component that queried this endpoint and used the parsed responseUrl as the src for an img tag (set via the components state). You'll also see below on the initial page load I have the url state query the cat as a service endpoint - just for some added variety:

import React, { Component } from "react"; 
import styled from "styled-components";
import axios from "axios"; const CatPic = styled.imgmargin: auto; background-color: white; max-width: 60vw; max-height: 60vh; margin: 2em; border-radius: 10px; &amp;:hover { cursor: pointer; } border: 0.2em solid lightgrey;
; class Cat extends Component { constructor() { super(); this.state = { url: "https://cataas.com/cat"; //default image - calling another cat api! }; this.getCat = this.getCat.bind(this); } getCat() { axios .get("/cat") .then(data => this.setState({ url: data.data })) .catch(data => console.log(data)); } render() { return ( <div> <CatPic src={this.state.url} onClick={this.getCat} /> <div /> </div>
); }
} export default Cat;

Heroku Integration

So up to this point when working on the project I was commiting all the changes to my local GitHub repository and had not selected a platform for serving the application. I had several services I wanted to try out including Heroku, Firebase and AWS static hosting. In order to go for an easy and cheap hosting solution I ended up trying out Heroku.

I setup my Heroku project and configuring it so that when I made a new commit to my master branch on my project it would auto deploy a new build and expose the service publically. Ideally in the future I'm going to add in some CircleCI testing and protected master branch settings to prevent directly pushing changes to the master branch. For the purposes of this side project the current setup is fine but any errors pushed will go live on the site almost instantly. Instructions for setting up some of this configuration are available over on Heroku if you have any questions on that process feel free to comment below.

The End Result

The application is currently exposed live over here. The codebase is available here if you're interested in having a look or using it as a boilerplate of sorts for your own create-react-app / Express hybrid application!

Further Improvments

Theres a bunch that can be improved about the current implementation including:


  • Use of node development environments to auto switch urls being used for express communication when developing the service locally

  • Alternate between APIs being called in backend

  • Processing of data returned by APIs -> generally response is an image bytestream or some similar structure that may be processible into an image displayed directly on the main page

  • Security Cert Bug - seems on some Mac devices Chrome has some issue related so the SSL cert on the page, I suspect this can be fixed with some additional Heroku configuration

Your Thoughts & Questions?

Feel free to drop and thoughts or questions you have below. I'd be interested to see if the above is useful to anybody starting out with react and looking to start using services like express with create-react-app.

There are a variety of boilerplates available out there for use that are better than this setup, however the aim of the project was to show how to go from the basic create-react-app to something a bit more complex using an Express backend. This is a very simple example of calling an API in the backend, but you can imagine how this can be taken to the next level to do more complex calculations or communications to private services via the Express server.

Till next time!


Tag cloud