Using Nodemon to Automate Server Reloads

The first step to building my fullstack bookmark application is learning about backend development. While testing out basic concepts in Node.js and Express, I found myself constantly terminating jobs only to restart them a few moments later after making minor changes to my code. Going through this process every time I wanted to see anything updated became pretty annoying fairly quickly.

I decided to install nodemon to automate the process of reloading my server after changes had been made.

I decided to install it as a dependency first and run it as an npm script.
npm install --save-dev nodemon

Instead of using npm start, I used npx with the npx package runner instead.

After a successful local installation, I decided to install nodemon globally.
npm install -g nodemon

This way, in addition to being able to use nodemon for any file, I could also save myself the time of writing npm start or npx prior to nodemon.

Modularizing an Exercise App for Future Feature Development

Prior to the pandemic, I went to the gym every weekday and ended my workouts with a cooldown stretch. Depending on which muscle groups I used, I followed a particular series of stretches. I could never quite memorize the stretches, so I wound up going between the stopwatch app on my phone and the stretches in my web browser. If my phone memory wasn’t feeling generous, this would cause my browser to totally refresh (requiring me to scroll down to find the correct stretch) and my stopwatch to lag before displaying the correct time.

I decided to fix this problem by streamlining the process with a web application that would display the stretch I was currently doing, the stretch I would do next, and the amount of time remaining before it was time to move to that next stretch.

The process was straightforward. I built the structure of the app in HTML using BEM architecture and created a stylesheet using Sass as a processor. I then used vanilla JavaScript to give the app its functionality before using ESLint and Prettier.

The app functioned as I originally envisioned it, but I wanted to leave room for improvement in the future.

For one thing, I wanted to be able to easily add a new stretching routine to the application. To make this as modular as possible, I put each individual routine into a separate object in its own JavaScript file. This way, I could continue to use the same variables and classes for each new routine while automatically updating the name and image used for the exercise.

Secondly, different routines had different timers. For example, the stretches in the leg routine are supposed to be held for at least 1 minute, but the stretches in the arm routine are supposed to be held for about 30 seconds. I isolated the variables that controlled the time for the timer function and also put them in their own separate file. I decided it would be better to have multiple smaller files that were all different from each other than to have a smaller number of larger files with copied and pasted code.

Finally, I knew I wanted to change the functionality of the timer at some point in time. Instead of only being able to start the timer, I wanted to be able to pause the timer as well (like a stopwatch). I wasn’t sure exactly how I wanted to implement this yet, but by isolating the other components of the application, I felt confident that I would be able to easily update the function of the timer at a later point in time.

The most current version of this app is currently available on my website.

Creating User Stories for a MERN Stack TikTok Bookmark Organizer

During quarantine, TikTok became one of my new favorite sources for entertainment. A never ending series of relatively quick (and mostly fun) videos on a wide variety of topics is a fun way to pass the time. One feature TikTok has is the ability to “like” videos and store these videos within a folder on the app. However, after several months of liking videos, earlier bookmarks that I wanted to return to were buried very deeply within this folder. The app has no way of sorting these videos or organizing them into folders. After becoming frustrated by not being able to find some of my older bookmarked videos, I decided to build my own external TikTok bookmark organizer.

I knew that I would need a backend for an application like this. As I already knew some JavaScript, I decided to learn (and implement the app with) NodeJS, Express, and mongoDB to make a CRUD application. Before diving deep into learning about these concepts, I decided to create a series of requirements to pinpoint what I wanted to build.

  • As a website visitors, I want to be able to log in to or register an account to save my videos and bookmark folders.
  • As a registered user, I want to be able to enter URLs for TikTok videos and save these into a default “master” bookmark list.
  • As a registered user, I want to be able to permanently delete saved videos that I don’t like from the master list.
  • As a registered user, I want to be able to create, rename, and delete bookmark folders. I want to be able to add videos to these folders from my master list. I still want these videos to appear in my master list. I want to be able to add videos directly to a bookmark folder and have the video appear in the master list as well.
  • As a registered user, I want to be able to add multiple videos to one bookmark folder simultaneously.
  • As a registered user, I do not want to see duplicate videos in my bookmark folders or my master list.
  • As a registered user, I want to be able to delete video from a bookmark folder without deleting it from the master list of videos.
  • As a registered user, I want to be able to change the order of videos in a bookmark folder.
  • As a registered user, I want to be able to search my master list using tags and/or video descriptions that TikTok content creators have added.
  • As a registered user, I want to be able to share a bookmark list with a friend who does not have an account.
  • As a user, if a video that I saved is no longer available on TikTok, I should be able to see name of the content creator and the description for the video.
  • As a desktop user, I want to be able to view my saved TikToks in the same browser window.
  • As a mobile user, I want to be redirected to the TikTok application (or to the TikTok website) to view videos.

There’s a lot in front of me to tackle, but I’m looking forward to breaking down each user story into technical requirements and learning how to implement them!

Automating My Project Setup with NPM Scripts

When I create a web application, I typically follow the same steps every time. I create a new directory in my projects folder, open that folder in VSCode, and then create three files: index.html, index.js, and style.scss. After reading about npm Scripts, I wanted to automate these actions and set up new projects from the command line.

After learning more about the package.json file, I wanted to find a way to use scripts to do the following three things in the terminal:

  1. Create a new directory
  2. Navigate into that directory
  3. Create three new files: index.html, index.js. and style.scss

I knew I would use mkdir to create a new directory, cd to enter into that directory, and touch to create the three files. However, I needed to give the folder a name, and that would require me to pass some sort of argument to create the directory (and to navigate to it).

I read a blog post by Dominik Kundel that explained passing arguments and parsing in npm Scripts. I could create an environment variable inside of my package.json file that I could later pass as an argument in the terminal. To create an environment variable, I had to use the following format: $npm_config_foo. I could parse this argument in the terminal using the following syntax: --foo=bar.

With this in mind, I created a script called project and tested it in the terminal.

"scripts": {
"project": "mkdir $npm_config_name && cd $npm_config_name && touch index.html index.js style.scss"
},

Testing out npm Scripts

Page Height on Mobile Devices

Before deploying my application, I tested it using the developer tools available on Chrome. It looked exactly as I envisioned it when testing it across a myriad of screen sizes. However, after I visited the application on my mobile phone, I realized that there was one very big problem. The information at the bottom, which included the music playback option, the temperature, the date, the day, and time, was missing.

「失望落胆」 means being despondent (Yes, the app is goading me.)

There was no option to scroll further either. I set the height of the page to 100vh in CS and the grid layout I used that encompassed the entire <body> node assigned 7vh to the footer and 93vh to the rest of the content on the page. I designed the application this way to be as responsive as possible across devices. It worked exactly as it should have but had the unintended side effect of rendering the content at the bottom of the page invisible.

Changing the distribution of the grid to 17vh and 83vh fixed the problem on mobile, but, naturally changed the layout for larger screens. I considered doing a media query based on screen size, but the problem was not the size of the screen so much as it was the presence of the mobile browser’s status bars. When scrolling through longer pages, these status bars disappear, but, given how compact the content of the page was, there was nothing to scroll through to allow the status bars to disappear.

Instead of setting the height of the <body> node as 100vh or 100%, I wanted to set it to the height of the screen actually available to the user. To find this number, I would need to go into the JavaScript for the app, as opposed to the CSS, and find the find the innerHeight of the page. I could then set the height of the page to the innerHeight. This would make the design of the page resilient to things like browser status bars that would have otherwise hidden content.

const innerViewportHeight = window.innerHeight;
page.style.height = `${innerViewportHeight}px`;

I also changed the stylesheet of the page to measure the grid layout in percentages as opposed to the vh unit.

「孤雲野鶴」 means hermit (literally a cloud that has broken off, a crane that has left the flock to frolic in the field)

Creating a Japanese Idiom Web Application

I created a web application that would use two different APIs (one for the idioms, a separate one for the weather) to allow the user to “start their day” with a piece of wisdom and knowledge about the current weather conditions.

This was my first time integrating APIs with a web application, and I look forward to using some of the many APIs available free of charge. Learning how to use APIs using fetch and then extracting data from those APIs opened up a world of possibilities for exploring different types of data. I also used ESLint and Prettier again before pushing my code to GitHub.

I want to return to this application in the future with further knowledge of databases. My original idea for this application was using one API to get an idiom and its reading and then using a separate API to get the definition of that idiom. Unfortunately, I could not find an API that would allow me to get the Japanese definition of Japanese idioms. I would like to create my own database with JSON that includes an idiom, its meaning, and its Japanese definition (and a source for this information). It would also be nice to use some of my translation skills and provide an English equivalent when possible and a literal English translation when not.

I would also like to return to this application after learning some Node.js and Express to create a proxy server to protect my API key. Although only the Open Weather Application requires an API key (and the API key I chose was provided free of charge), I would still like this data to be more protected in the future.

The live implementation of the Japanese idiom web application is available now on my website: https://mcksheridan.com/projects/idioms.

「唯一不二」 means “one and only”

Using Media Queries in JavaScript

Having videos as backgrounds would not be in the best interest of mobile users, who would likely be trying to conserve data usage. To avoid having videos take up any bandwidth whatsoever, I decided to remove the video element from my HTML altogether and use a media query in JavaScript to add it only for users on larger screens. I decided to make the media query using a min-device-width query.

const mediaQuery = window.matchMedia('(min-device-width: 1200px)')

I then created an if statement that would be satisfied if the device width was at least 1,200 px. If that condition was satisfied, then a <video> element would be added within the empty video <div> in the HTML. Then, the source for the video would be changed depending on the weather condition.

if (mediaQuery.matches) {
video.innerHTML = '<video src="" type="video/mp4" class="video_background" autoplay loop muted></video>';
const videoBackground = document.querySelector('.video_background');
videoBackground.src = 'backgrounds/' + weather + '.mp4';
}

I also decided that it would be much more efficient to generate the correct URL for the weather video without the if statement I used previously. Instead, I renamed the videos so that they matched the names of the weather status pulled from the API. This way, I was able to implement the same functionality with far fewer lines of code.

I implemented the same concept for backgrounds for users on smaller screens. Instead of having a video in the background, they would see an image file of the current weather conditions.

page.style.backgroundImage = "url('backgrounds/" + weather + ".jpg'";

If the user was unable to see the video background on their larger screen, they would still be able to view the current weather conditions via the background JPEG image.

Using Session Storage for API Data

After fetching the idiom API from the server and adding it to the document, I realized I wanted the user to see the same idiom until they closed their browser. To accomplish this, I decided to use sessionStorage.

Initially, I fetched the API and used stringify to convert the JSON data into a string that sessionStorage could store.

fetch('https://corsservice.appspot.com/yojijukugo/api/')
.then(response => response.json())
.then(data => { sessionStorage.setItem('jukugo',JSON.stringify(data['jukugo'])); sessionStorage.setItem('yomi',JSON.stringify(data['yomi'])); yojijukugoKanji.innerText = JSON.parse(sessionStorage.getItem('jukugo')); yojijukugoKana.innerText = JSON.parse(sessionStorage.getItem('yomi'));

However, ever after using setItem and getItem for the idiom, the idiom would still change upon refresh. The code I had written was trying to fetch the data and set and get a new item upon each page reload. This was not what I wanted.

To fix this, I decided to write a function that checked to see if sessionStorage.getItem existed for the two parts of the idiom (jukugo and yomi). If it existed, I wanted to use the data that already existed without fetching anything. If the value was null, that meant the data had not been fetched yet.

if (((sessionStorage.getItem('jukugo')) !== null) && ((sessionStorage.getItem('yomi')) !== null)) { yojijukugoKanji.innerText = JSON.parse(sessionStorage.getItem('jukugo')); yojijukugoKana.innerText = JSON.parse(sessionStorage.getItem('yomi'));

If the user had not visited within this browser session, the original code that used fetch, get, and set would be implemented instead with an else statement. That data would then persist across their browser session. Success!

Weather Backgrounds with the Open Weather Map API

Despite studying Japanese for nearly a decade now, I still struggle to understand and remember yojijukugo, or idioms comprised of four Chinese characters. For my next project, I decided it would be fun to build an app to start my day off with a different idiom. Aside from the idiom (and its pronunciation and definition), I wanted to add in a few other features for starting my day. Namely, I wanted to know the day, the date, the time, the temperature, and the current weather.

To find the weather, I used an API offered by Open Weather Map. I referenced their list of weather conditions and found looping videos that corresponded to the groups of weather conditions listed on Videezy. I thought it would be fun for the background to signal current weather conditions as opposed to a more traditional weather icon.

To make a video the background for the webpage, I added a <video> element into the HTML.

<video src="backgrounds/clearsky.mp4" type="video/mp4" class="video_background" autoplay loop muted></video>

In the CSS I styled the video so that it would extend fully to cover the entire page and keep it in the background.

.video_background {
position: absolute;
top: 0;
left: 0;
min-height: 100%;
min-width: 100%;
z-index: -1;
}

I used fetch to get the API and then created a variable that corresponded to the data from the icon key in the object from the API. There were nine different descriptions that had icons available, so I saved nine different videos to use as backgrounds. (Some descriptions had both day and night icons available, but I only saved one video per description.) I created a conditional statement that would set a background depending on the leading two digits of the icon.

const weather = data['weather'][0]['icon'];
function weatherBackground () {
if (weather.startsWith('02')) {
videoBackground.src = 'backgrounds/fewclouds.mov';
} else if (weather.startsWith('03')) {
videoBackground.src = 'backgrounds/scatteredclouds.mov';
} else if (weather.startsWith('04')) {
videoBackground.src = 'backgrounds/brokenclouds.mp4';
} else if (weather.startsWith('09')) {
videoBackground.src = 'backgrounds/shower_rain.mov';
} else if (weather.startsWith('10')) {
videoBackground.src = 'backgrounds/rain.mov';
} else if (weather.startsWith('11')) {
videoBackground.src = 'backgrounds/thunderstorm.mov';
} else if (weather.startsWith('13')) {
videoBackground.src = 'backgrounds/snow.mp4';
} else if (weather.startsWith('50')) {
videoBackground.src = 'backgrounds/mist.mp4';
}
}
weatherBackground();

Analyzing Code with ESLint and Prettier

Over the past few days, I’ve developed a word counter using HTML, CSS, and vanilla JavaScript. I tried my hand at BEM architecture; variables, nesting, modules, inheritance, and parent selectors in Sass; and regular expression and DOM manipulation in JavaScript. Before pushing my project to GitHub, I decided to try using ESLint and Prettier in VSCode with the Air BnB style guide. Although this page counter was very small, I wanted to make sure its code was as clean as possible.

Most of the suggestions that ESLint and Prettier gave were very easy to implement. However, there was on suggestion ESLint made that caused me to pause.

Unexpected surrogate pair in character class. Use 'u' flag.

The piece of code in question was the word counter. In order to add recognition for Japanese characters, I used several Unicode blocks.

countWord.innerText = text.match( /[\u3041-\u3096\u30A1-\u30FA々〇〻\u3400-\u9FFF\uF900-\uFAFF\uD840-\uD87F\uDC00-\uDFFF\w-']+/g ).length

I reviewed the documentation for the rule and decided to rewrite the code using the u flag to signal Unicode characters. This was the course of action recommended for surrogate pairs. I also changed the hyphen and apostrophe to Unicode. I didn’t think I had any surrogate pairs, so I wasn’t terribly surprised when the following code also produced an error.

/[\u{3041}-\u{3096}\u{30A1}-\u{30FA}\u{3005}\u{3007}\u{303B}\u{3400}-\u{9FFF}\u{F900}-\u{FAFF}\u{D840}-\u{D87F}\u{DC00}-\u{DFFF}\u{0027}\u{002D}\w]+/gu

I returned to the documentation again and realized that the Unicode characters I included in my Unicode blocks included “characters with combining characters,” or characters that belonged to the Mc, Me, or Mn Unicode general categories. I took a look at the blocks and the ranges of Unicode characters they provided and realized that I \u{DC00}-\u{DFFF} was a match for every existing Unicode character. I removed this from my code and the error in ESLint disappeared. With that segment of code gone, I followed up by referencing a list of Unicode characters from 3000 to 3FFFF and adding a few more individual Unicode characters for Japanese symbols that occur within words.

/[\u{3005}-\u{3096}\u{30A1}-\u{30FF}\u{303B}\u{3400}-\u{9FFF}\u{F900}-\u{FAFF}\u{D840}-\u{D87F}\u{0027}\u{002D}\w]+/gu

With the code bug-free, I uploaded my files to GitHub and deployed a live version on my website.