Tag: React

  • A small open source contribution never hurt anyone

    Over the weekend, I started using the react-runtime-config library to configure one of my React apps. While it works great, the last activity on the library was over two years ago, so I decided to fork the repository and publish the library under my own scope. This led me down a very eccentric path and opened a number of new doors.

    Step One: Automate

    I wanted to create an automated build and release process for the library. The original repository uses TravisCI for builds, but I much prefer having the builds within Github for everyone to see.

    The build and publish processes are pretty straight forward: I implemented a build pipeline which is triggered on any commit and runs a build and test. The publish pipeline is triggered on creating a release in Github, and runs the same build/test, but updates the package version to the release version and then publishes the package to npmjs.org under the @spydersoft scope.

    Sure, I could have stopped there… but, well, there was a ball of yarn in the corner I had to play with.

    Step 2: Move to Yarn

    NPM is a beast. I have worked on projects which take 5-10 minutes to run an install. Even my little test UI project took about three minutes to run an npm install and npm build.

    The “yarn v. npm” war is not something I’d like to delve into in this blog. If you want more detail, Ashutosh Krishna recently posted a pretty objective review of both over on Knowledge Hut. Before going all in on Yarn with my new library, I tested Yarn on my small UI project.

    I started by deleting my package-lock.json and node_modules folder. Then, I ran yarn install to get things running. By default, Yarn was using Yarn 1.x, so I still got a node_modules folder, but a yarn.lock file instead of package-lock.json. I modified my CI pipelines, and I was up and running.

    On my build machine, the yarn install command ran in 31 seconds, compared to 38 seconds for npm install on the same project. yarn build took 34 seconds, compared to 2 minutes and 20 seconds for npm build.

    Upgrading to modern Yarn

    In my research, I noted that there are two distinct flavors of yarn: what they term “modern versions” of Yarn, which is v2.x and above, and v1.x. As I mentioned, the yarn command on my machine defaults to the 1.x version. Not wanting to be left behind, I decided to migrate to the new version.

    The documentation was pretty straight forward, and I was able to get everything running. I am NOT yet using the Plug and Play installation strategy, but I wanted to take advantage of what the latest versions have to offer.

    First Impressions

    I am not an “everyday npm user,” so I cannot speak to all the edge cases which might occur. Generally, I am limited to the standard install/build/test commands that are used was part of web development. While I needed to learn some new commands, like yarn add instead of npm install, the transition was not overly difficult.

    In a team environment, however, moving to Yarn would require some coordination between the team to ensure everyone knows when changes would be made and avoid situations where both package managers are used.

    With my small UI project converted, I was impressed enough to move the react-runtime-config repository and pipelines to use Yarn 3.

    Step 3: Planned improvements

    I burned my allotted hobby time on steps 1 and 2, so most of what is left is a to-do list for react-runtime-config.

    Documentation

    I am a huge fan of generated docs, so I would like to get some started for my version of the library. The current readmes are great, but I also like to have the technical documentation for those who want it.

    External Configuration Injection

    This one is still very much in a planning stage. My original thought was to allow the library to make a call to an API to get configuration values, however, I do not want to add any unnecessary overhead to the library. It may be best to allow for a hook.

    I would also want to be able to store those values in local storage, but still have them be updatable. This type of configuration will support applications hosted within a “backend for frontend” API, and allow that API to pass configuration values as needed.

    Wrapping up

    I felt like I made some progress over the weekend, if only for my own projects. Moving react-runtime-config allowed me to make some general upgrades to the library (new dependency updates) and sets the stage for some additional work. My renewed interest in all things Node also stimulated a move from Create React App to Vite, which I will document further in an upcoming post.

  • Update: Creating an Nginx-based web server image – React Edition

    This is a short update to Creating a simple Nginx-based web server image which took me about an hour to figure out and 10 seconds to fix…..

    404, Ad-Rock’s out the door

    Yes, I know it’s “four on the floor, Ad-Rock’s at the door.” While working on hosting one of my project React apps in a Docker image, I noticed that the application loaded fine, but I was getting 404 errors after navigating to a sub-page (like /clients) and then hitting refresh.

    I checked the container logs, and lo and behold, there were 404 errors for those paths.

    Letting React Router do its thing

    As it turns out, my original Nginx configuration was missing a key line:

    server { 
      listen 8080;
      server_name localhost;
      port_in_redirect off;
      
      location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    
        # The line below is required for react-router
        try_files $uri $uri/ /index.html;
      }
      error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }

    That little try_files line made sure to push unknown paths back to index.html, where react-router would handle them.

    And with that line, the 404s disappeared and the React app was running as expected.

  • Configuring React SPAs at Runtime

    Configuring a SPA is a tricky affair. I found some tools to make it a little bit easier, but it should still be used with a fair amount of caution.

    The App

    I built a small React UI to view some additional information that I am storing in my Unifi Controller for network devices. Using the notes field on the Unifi device, I store some additional fields in JSON format in order for other applications to use. It is nothing wild, but allows me to have some additional detail on my network devices.

    In true API-first fashion, any user-friendly interface is an afterthought… Since most of my interaction with the service is through Powershell scripts, I did not bother to create the UI.

    However, I got a little tired of firing up Postman to edit a few things, so I spun up a React SPA for the task.

    Hosting the SPA

    I opted to host the SPA in its own container running Nginx to host the files. Sure, I could have used thrown the SPA inside of the API and hosted it using static files, which is a perfectly reasonable and efficient method. My long-term plan is to create a new “backend for frontend” API project that hosts this SPA and provides appropriate proxying to various backend services, including my Unifi API. But I want to get this out, so a quick Nginx container it is.

    I previously posted about creating a simple web server image using Nginx. Those instructions (and an important update for React) served me well to get my SPA running, but how can I configure the application at runtime? I want to build the image once and deploy it any number of times, so having to rebuild just to change a URL seems crazy.

    Enter react-runtime-config

    Through some searching, I found the react-runtime-config library. This library lets me set configuration values either in local storage, in a configuration file, or in the application as a default value. The library’s documentation is solid and enough to get get you started.

    But, wait, how do I use this to inject my settings??? ConfigMaps! Justin Polidori describes how to use Kubernetes ConfigMaps and volume mounts to replace the config.js file in the container with one from the Kubernetes ConfigMap.

    It took a little finagling since I am using a library chart for my Helm templating, but the steps were something like this:

    1. Configure the React app using react-runtime-config. I added a config.js file to the public folder, and made sure my app was picking settings from that file.
    2. Create a ConfigMap with my window.* settings.
    3. Mount that ConfigMap in my container as /path/to/public/config.js

    Viola! I can now control some of the settings of my React App dynamically.

    Caveat Emptor!

    I cannot stress this enough: THIS METHOD SHOULD NOT BE USED FOR SECRET OR SENSITIVE INFORMATION. Full stop.

    Generally, the problem with SPAs, whether they are React, Angular, or pick your favorite framework, is that they live on the client in plain text. Hit F12 in your favorite browser, and you see the application code.

    Hosting settings like this means the settings for my application are available just by navigating to /config.js. Therefore, it is vital that these settings are not in any way sensitive values. In my case, I am only storing a few public URLs and a Client ID, none of which are sensitive values.

    The Backend for Frontend pattern allows for more security and control in general. I plan on moving to this when I create a BFF API project for my template.

  • React in a weekend…

    Last week was a bit of a ride. My wife was thrust into her real estate career by a somewhat abrupt (but not totally unexpected) reduction in force at her company. We spent the middle of the week shopping to replace her company vehicle, which I do not recommend in the current market. I also offered to spend some time on standing up a small lead generation site for her so that she can establish a presence on the web for her real estate business.

    I spent maybe 10-12 hours getting something running. Could it have been done more quickly? Sure. But I am never one to resist the chance to setup deployment pipelines and API specifications. I figured the project would run longer than 5 days.

    Why only 5 days? Well, Coldwell Banker (her real estate broker) provides a LOT of tools for her, including a branded website with tie in’s to the MLS system. So I forwarded www.agentandalyn.com to her website and my site will be digitally trashed.

    Frameworks

    For familiarity, I chose the .Net 5 ASP.NET / React template as a starter. I have a number of API projects running so I was able to utilize some boilerplate code from those projects to configure Serilog to my internal ELK stack and basic authentication to my Identity Server. The above tutorial is a good start to getting things moving forward with the site.

    On the React app site, I immediately updated all the components to their latest versions. This included moving Bootstrap to version 5. Reactstrap is installed by default, but does not have support for Bootstrap 5. I could have dropped Reactstrap in favor of the RC version of React-Bootstrap, but I’m comfortable enough in my HTML styling, so I just used the base DOM elements and styled them with the Bootstrap styles.

    It probably took me an hour or so to take the template code and turn it into the base for the home page. And then I built a deployment pipeline…

    A Pipeline, you say..

    Yes. For what amounts to a small personal project, I built an Azure DevOps pipeline that builds the project and its associated Helm chart, publishes the resulting docker image and Helm chart to feeds in Proget, and initiates a release in Octopus Deploy.

    While this may seem like overkill, I actually have most of this down to a pretty simple process using some standard tools and templates.

    Helm Charts made easy

    For the Helm charts, I utilizes the common chart from k8s-at-home’s library-charts repository. This limits my Helm chart to some custom values in values.yaml file to define my image, services, ingress, and any other customizations I may want.

    I typically use a custom liveness and readiness probe that lets me hit a custom health endpoint served up using ASP.NET Core’s Custom Health Checks. This allows me some control to add more than just a ping check for the service.

    Azure DevOps Pipelines

    As mentioned in more than one previous post, I am thoroughly impressed with Azure DevOps Build Pipelines thus far. One of the nicer features is the ability to save common steps/jobs/stages to a separate repository, and then re-use those build templates in other pipelines.

    Using my own templates, I was able to construct a build pipeline that compiles, creates and publishes a docker image, creates and publishes a Helm chart, and creates and deploys an Octopus Release in a 110 line YAML file.

    Octopus Project / Release Pipeline

    I have been experimenting with different ways to deploy and manage deployments to Kubernetes. While not the fanciest, Octopus Deploy does the job. I am able to execute a single step to deploy the Helm chart to the cluster, and can override various values with Octopus variables, meaning I can use the same process to deploy to test, stage, and production.

    Wasted effort?

    So I spent a few days standing up a website that I am in the middle of deleting. Was it worth it? Actually, yes. I learned more about my processes and potential ways to make them more efficient. It also spiked my interest in putting some UIs on top of some of my API wrappers.