Create React App (CRA) over HTTPS Ingress

A brief history of a use case request.By vicjicama

Introduction

This time I will write about having the CRA working over HTTPS, this use case is special for me because that was one of the cases that I was trying to accomplish in the early days of my React/Node/Microservices journey. Time to try again... older, wiser and with new tools.

The Request

The CTO of a startup contacted me after looking at the post about bring your cluster services to the localhost, he told me that he liked the simple approach and if this works as claimed or as he understands... this could be helpful for his team. We were talking about different use cases and one among them is inspiring case for this post: Can you have the CRA working on Kubernetes?

After the call I was thinking about the possibilities and benefits of having you local CRA with the reload over HTTPS with your domain name:

First thoughts...

I was thinking on the general steps that are needed to have the CRA working over https, this would dependent on the proxy method that you are using for the ingress/proxy, but I came out with the next steps:

The way you expose your CRA following the previous steps vary from stack to stack, In this example I will be using an nginx Kubernetes ingress, but the concepts and mechanisms should be the same for other proxy methods or stacks.

The CRA

You can skip this section if you already have a CRA up and runnning on you localhost.

The next are the steps to setup the CRA boilerplate on your local env. Go to a folder and execute the next commands to create a folder and install the create-react-app package

mkdir cra && cd cra
  yarn init -y
  yarn add create-react-app

After this go to the file package.json and add the highlighted lines (9-11) and save the file.

{
    "name": "cra",
    "version": "1.0.0",
    "main": "index.js",
    "license": "MIT",
    "dependencies": {
      "create-react-app": "^3.3.0"
    },
    "scripts": {
      "create": "create-react-app react-local"
    }
  }

Execute the next commands to create and start the app boilerplate:

yarn run create
  cd react-local
  yarn start

To clean up this test you just need to delete the create cra folder.

You should have your CRA up and running as in the image, In our example we are going to do as the image says... Edit src/App.js and save to reload.

HTTPS Troubleshooting

You are going to have the error on the picture if you try to access the forwarded service using the HTTPS ingress endpoint, there is an issue with the latest version of CRA and the Websocket connection.

I saw how to solve this issue on this github issue: (thanks for the solution justingrant@github)

Go to your cra folder and execute the next commands, those will create a backup of the webpackHotDevClient.js file and modify it with the needed patch.

cp node_modules/react-dev-utils/webpackHotDevClient.js \
node_modules/react-dev-utils/webpackHotDevClient.js.bak
sed -i "s/protocol: 'ws'/protocol: window.location.protocol === 'https:' ? 'wss' : 'ws'/g" \
node_modules/react-dev-utils/webpackHotDevClient.js
        

The Kubernetes ingress

If you already have an ingress with https and a service in your cluster you can skip this section, any ingress/service will work.
Here Kubernetes is used as the example cluster platform, the same principle will work if you have your services handle by docker-compose for example, at the end of the day the ingress is a nginx proxy running on the cluster. (the only thing that should change is the forward target, for example the upstream host:port for a raw nginx proxy)

Here is the definition of the ingress, the service that is going to be "replaced" is home-web-service:3000 ingress.yaml

apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    name: microservice-home-ingress
    namespace: repoflow-blog-namespace
  spec:
    tls:
      - hosts:
          - blog.repoflow.com
        secretName: certificates-secret
    rules:
      - host: blog.repoflow.com
        http:
          paths:
            - path: /backend
              backend:
                serviceName: home-graph-service
                servicePort: 4000
            - path: /
              backend:
                serviceName: home-web-service
                servicePort: 3000

I don't have a yaml file for the secret certificates-secret because this one is generated by a kubernetes CronJob.

All the details of the CronJob and certificate generation and renew are our of the scope of this post, you can find all the code on the microservices repositories, I get the tls.crt and tls.key strings from the generated Letsencrypt certificates. The generated secret looks like this:

const ssl_secret = {
      kind: "Secret",
      apiVersion: "v1",
      metadata: {
        name: SSL_SECRET_NAME,
        namespace: NAMESPACE
      },
      data: {
        ["tls.crt"]: contFile64,
        ["tls.key"]: contKey64
      },
      type: "Opaque"
    };

The example is based in out lab/blog, it's a cluster that is up and running that you can visit, I version everything that goes into this cluster and it is open-source, you can find the code and the details on this repository: microservices and you can run it yourself in your minikube instance. I will use this cluster/app for the examples during this post.

Replace your cluster service with the local CRA

For the demo I will be using a tool that we did to help developers handle the forward of their local services to the cluster, you can find it here: linker-tool . Under the hood all the operations performed by the UI are auto SSH port tunnels (sometimes -L sometimes -R), you can have the same results if you do the service forwards manually. One of the goals of the linker-tool is to help team members that does not have any experience with ssh tunnels or kubernetes bring container services to their localhost and from their localhost to the cluster.

Here are the summary of the steps (same as in the video 1.23 min):

If you are a React frontend developer...

One think that I was thinking a lot while I was writing this post is to imagine what would be the thoughts of a React frontend while he reach the Kubernetes ingress section, if you are not familiar with Kubernetes, the ingress, the SSH tunnels or your stack is different don't hesitate to contact me, I will be glad to help you achieve the same results with you stack.

An additional cool thing is that since you already have you ingress resolved on your localhost, you can access other services from the cluster, in my case I query my graphql services on my SPA with the next url https://blog.repoflow.com/backend/graphql, now every SPA that I am testing locally can access this and other cluster endpoints locally.


You had my curiosity... but now you have my attention

I contacted again the CTO to review this solution and other use cases discussed. Overall he liked the solution and now he have a better idea of what we are doing and how it works, but that he needs to try it on his stack (they are not using minikube, they are using traefik and their cloud provider is different). He will try the approach and will give me feedback.

Conclusion

I think that having a CRA that can be shared is a very useful thing as a React developer, I am using React for all my frontends but I was not using CRA other than to adjust the the look and feel of components, but now with all the discussion and new point of views I have a lot of ideas for it on the future!

A complication to implement the reviewed approach on Kubernetes or any other stack is that this requires some configuration of the proxy/ingress of your stack, something that maybe as a frontend you are not familiar... but this could be solved with some help of your infrastructure team or CTO.

If as a frontend you ever felt like a fish out of water with containers, Kubernetes or docker-compose because of the impact to your local development workflow... then bring the container services to your localhost might be helpful to keep using your local workflow as always.

Previously I wrote about the cluster to local use cases here: A Fullstack working with Microservices, I am working on a more complete post with more local to cluster use cases, stay tuned! :)

If you want to save time to your team forwarding your local service to the cluster give it a try to the tool that we use during the use case of this post: linker-tool

Thanks for reading, if you have any feedback, any question, any use case to discuss or if you want to reach out don't hesitate to contact me, my email is vic@repoflow.com