I am a lazy developer or How to write 438 lines of nginx redirects

hryggrbyr

Thomas Rigby

Posted on June 9, 2021

I am a lazy developer or How to write 438 lines of nginx redirects

When I say "I am a lazy developer", I don't mean to imply that I cut corners and churn out shoddy code. I just hate doing repetitive tasks that bore the bejesus out of me!

Obviously, I'm not alone in this - it's the reason task runners exist.

Recently, at work, I replatformed an existing e-commerce site which resulted in, amongst other things, a giant list of redirects from existing urls to the new url structure.

Permanent redirects (301s for the people who like numbers) are essential for persistence of good SEO. The downside is adding the old url and the new url to the line rewrite ^/<oldlocation>$ <newlocation> permanent; in my nginx config file. What's a lazy guy to do when you have to do this 438 times…?

Well, this immediately looks like a case for loops and variables!

How can you do this wizardry?!

You'll need four things;

  1. NodeJS installed on your machine,
  2. a command-line application like Hyper, iTerm2, or Terminal,
  3. a CSV file of the required redirects, and
  4. this handy class in an index.js file.
// excel.csv

old,new
https://thomasxbanks.com/newyork/, https://thomasxbanks.com/locations/newyork/
https://thomasxbanks.com/paris/, https://thomasxbanks.com/locations/paris/
https://thomasxbanks.com/peckham/, https://thomasxbanks.com/locations/peckham/


// index.js

const fs = require('fs');

class GenerateNginxRedirectsFromCSV {
  constructor(input, output) {
    this.input = input || './input.csv';
    this.output = output || './output.txt';
    this.csv = null;
    this.results = [];
  }

  async read() {
    this.csv = await fs.readFileSync(this.input, { encoding: 'utf8', flag: 'r' });
  }

  async format() {
    this.results = this.csv.replace(/\n/g, '').split('\r').filter(Boolean).slice(1).map((x) => `rewrite ^/${x.split(',')[0]}?$ ${x.split(',')[1]} permanent;\n` );
  }

  write() {
    this.results.forEach(async (value) => {
      await fs.appendFileSync(this.output, value);
    });
  }

  async init() {
    await this.read();
    await this.format();
    await this.write();
  }
}

const task = new GenerateNginxRedirectsFromCSV('./excel.csv', './redirects.txt');
task.init();

Enter fullscreen mode Exit fullscreen mode

Put both files in the same folder, open the folder in your command line Terminal application and run node ./. This will generate a file (called output.txt unless you've changed it) listing your redirects in an easy-to-copypasta format. Paste the contents into your nginx.conf file.

Cool! How does it work?

There's a lot going on here so let's go through it.

const fs = require('fs');

Enter fullscreen mode Exit fullscreen mode

fs is the NodeJS File System module. I won't go into detail here but, basically, it allows you to Read from and Write to files on your local system, servers, or whereever Node is installed.

constructor(input, output) {
  this.input = input || './input.csv';
  this.output = output || './output.txt';
  this.csv = '';
  this.results = [];
}

Enter fullscreen mode Exit fullscreen mode

In the constructor, we set our scoped variables (including fallbacks) and the empty variables that will be populated by our fetched and formatted data.

async init() {
  await this.read();
  await this.format();
  await this.write();
}

Enter fullscreen mode Exit fullscreen mode

As we can see from the init() function, our three basic steps are;

  1. Get the contents of the CSV file
  2. Convert it into a format nginx can understand
  3. Write the results to a file

Step 1 - Get the contents of the CSV

this.csv = await fs.readFileSync(this.input, { encoding: 'utf8', flag: 'r' });

Enter fullscreen mode Exit fullscreen mode

Read the input filepath and save the contents into the this.csv variable for later use.

Step 2 - Convert CSV to nginx

Since the output of the csv file is consistent, and so is the format of a JSON object, we can map one to the other.

async format() {
  this.results = this.csv.replace(/\n/g, '').split('\r').filter(Boolean).slice(1).map((x) => `rewrite ^/${x.split(',')[0]}?$ ${x.split(',')[1]} permanent;\n` );
}

Enter fullscreen mode Exit fullscreen mode

Firstly, replace any \n line-endings, then explode the string into an array at each line-break (\r).

Then, we generate an array of results.

  • Filter out any empty lines with .filter(Boolean)
  • Remove the line with the headers using .slice(1)
  • For each remaining line, generate a string to copy into nginx.conf.

It should look a little like this;

rewrite ^/https://thomasxbanks.com/newyork/?$ https://thomasxbanks.com/locations/newyork/ permanent;

Enter fullscreen mode Exit fullscreen mode

Step 3 - Write the output file

Now that this.results is an array of strings, we can

  • loop through each instance
  • insert the result to an ouput file using fs.

All that's left to do is open the resulting file and copypasta the content into your nginx.conf file.

Don't forget to gracefully restart the nginx server. Forgetting this has caused me untold headaches!

nginx -t && nginx service restart
Enter fullscreen mode Exit fullscreen mode

Conclusion

Now, I'm sure there are different better ways to do this but, off the top of my head, this seemed quick and simple enough to whip together.

I've no idea how long it would have taken me to manually do this but I'm certain it would have taken longer than to write this bit of code.

Not only did I save myself time on that particular day, whenever I (or someone else on my team) need to do this again I have a useful tool to reuse again and again 😎

💖 💪 🙅 🚩
hryggrbyr
Thomas Rigby

Posted on June 9, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related