How to Build Your Own ReadMe Badge Generator with Express

If you‘ve browsed open source projects on GitHub, you‘ve likely noticed the eye-catching badges adorning many repositories‘ ReadMe files. From build status and test coverage to version number and license, these badges provide a quick way to convey key project metadata to visitors.

In this guide, we‘ll walk through how to build your own custom badge generator using Node.js and the Express web framework. By the end, you‘ll have a service that can generate SVG badges on the fly based on requested parameters. Let‘s get started!

What are ReadMe Badges?

ReadMe badges are small, dynamically generated images that are typically displayed at the top of a project‘s ReadMe file. They provide an at-a-glance view of important project information like:

  • Build status (e.g. "passing", "failing")
  • Test coverage percentage
  • Latest version number
  • License (e.g. "MIT", "Apache 2.0")
  • Download count
  • And more

Badges are usually served as SVG images, which can be easily embedded in Markdown files like the ReadMe. They are generated dynamically so they always display the latest data.

Some popular badge providers include Shields.io, Badgen, and For The Badge. However, you can also generate your own custom badges, which is what we‘ll do in this guide.

Setting Up an Express Server

To get started, let‘s set up a basic Express server. Make sure you have Node.js and npm installed, then create a new directory for the project:

mkdir badge-generator
cd badge-generator

Initialize a new npm project and install the necessary dependencies:

npm init -y
npm install express

Create a new file called index.js with the following code to set up the Express server:

const express = require(‘express‘);
const app = express();

app.get(‘/‘, (req, res) => {
  res.send(‘Badge generator‘);
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Badge generator listening on port ${port}`);
});

This sets up a basic Express server that listens on port 3000 (or the port specified by the PORT environment variable) and responds with "Badge generator" when the root URL is requested.

You can start the server by running:

node index.js

And visiting http://localhost:3000 in your browser. You should see the "Badge generator" message.

Creating the SVG Template

Next, let‘s create a template for our SVG badge. We‘ll use the Squirrelly template engine to dynamically insert the badge data into the SVG.

Install Squirrelly:

npm install squirrelly

Create a new file called badge-template.svg with the following code:

<svg xmlns="http://www.w3.org/2000/svg" width="{{=it.width}}" height="20">
  <linearGradient id="b" x2="0" y2="100%">
    <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
    <stop offset="1" stop-opacity=".1"/>
  </linearGradient>
  <mask id="a">
    <rect width="{{=it.width}}" height="20" rx="3" fill="#fff"/>
  </mask>
  <g mask="url(#a)">
    <path fill="#555" d="M0 0h{{=it.labelWidth}}v20H0z"/>
    <path fill="{{=it.color}}" d="M{{=it.labelWidth}} 0h{{=it.valueWidth}}v20H{{=it.labelWidth}}z"/>
    <path fill="url(#b)" d="M0 0h{{=it.width}}v20H0z"/>
  </g>
  <g fill="#fff" text-anchor="middle" font-family="sans-serif" font-size="11">
    <text x="{{=it.labelWidth / 2}}" y="14">{{=it.label}}</text>
    <text x="{{=(it.labelWidth + it.valueWidth / 2)}}" y="14">{{=it.value}}</text>
  </g>
</svg>

This template defines the structure of our SVG badge. It has a rounded rectangle background, a color-coded left side for the label, and a right side for the value. The {{= }} syntax is used to insert dynamic values into the template.

Generating Badges

Now that we have our template, let‘s update the Express server to handle requests for badges.

Update index.js with the following code:

const express = require(‘express‘);
const Sqrl = require(‘squirrelly‘);
const path = require(‘path‘);

const app = express();

app.get(‘/:label/:value/:color?‘, (req, res) => {
  const { label, value, color = ‘4c1‘ } = req.params;
  const labelWidth = label.length * 8 + 10;
  const valueWidth = value.length * 8 + 10;
  const width = labelWidth + valueWidth;

  const svg = Sqrl.renderFile(path.join(__dirname, ‘badge-template.svg‘), { 
    width,
    labelWidth, 
    valueWidth,
    color: `#${color}`,
    label,
    value
  });

  res.writeHead(200, {
    ‘Content-Type‘: ‘image/svg+xml;charset=utf-8‘,
    ‘Cache-Control‘: `max-age=${24*60*60}, immutable`,
  });
  res.end(svg);
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Badge generator listening on port ${port}`);
});

This code defines a route that handles requests for badges in the format /:label/:value/:color?. The label and value are required parameters, while color is optional and defaults to 4c1 (a shade of green).

It calculates the width of the label and value sections based on their character count, then renders the SVG template using Squirrelly, passing in the calculated widths, color, label, and value.

Finally, it sets the appropriate Content-Type header to image/svg+xml and the Cache-Control header to cache the badge for 24 hours. This improves performance by allowing clients to cache the badge instead of generating it on every request.

You can test the badge generator by starting the server and visiting a URL like:

http://localhost:3000/hello/world/ff0000

You should see a red badge with the label "hello" and value "world".

Adding Customization Options

Our basic badge generator is functional, but let‘s add some more customization options to make it more flexible.

First, let‘s define some preset color options that map to hex color codes. Add the following code above the route definition in index.js:

const colorPresets = {
  brightgreen: ‘4c1‘,
  green: ‘97ca00‘,
  yellow: ‘dfb317‘,
  yellowgreen: ‘a4a61d‘,
  orange: ‘fe7d37‘,
  red: ‘e05d44‘,
  blue: ‘007ec6‘,
  grey: ‘555‘,
  gray: ‘555‘,
  lightgrey: ‘9f9f9f‘,
  lightgray: ‘9f9f9f‘,
  critical: ‘e05d44‘,
  important: ‘fe7d37‘,
  success: ‘4c1‘,
  informational: ‘007ec6‘,
  inactive: ‘9f9f9f‘,
};

Then update the route to use the preset if the provided color matches one:

app.get(‘/:label/:value/:color?‘, (req, res) => {
  const { label, value, color = ‘4c1‘ } = req.params;
  const hexColor = colorPresets[color] || `#${color}`;
  // ...
  const svg = Sqrl.renderFile(path.join(__dirname, ‘badge-template.svg‘), { 
    // ...
    color: hexColor,
    // ...
  });
  // ...
});

Now you can use color names like "brightgreen" or "red" in addition to hex color codes.

We can also add support for custom logos to be displayed on the left side of the badge. First, update the SVG template to include a spot for the logo:

<svg xmlns="http://www.w3.org/2000/svg" width="{{=it.width}}" height="20">
  <!-- ... -->
  <g mask="url(#a)">
    <path fill="#555" d="M0 0h{{=it.labelWidth}}v20H0z"/>
    {{?it.logo}}
    <image x="5" y="3" width="14" height="14" xlink:href="{{=it.logo}}"/>
    {{/}}
    <!-- ... -->
  </g>
  <!-- ... -->
</svg>

Then update the route to accept a logo query parameter:

app.get(‘/:label/:value/:color?‘, (req, res) => {
  const { label, value, color = ‘4c1‘ } = req.params;
  const { logo } = req.query;
  // ...
  const svg = Sqrl.renderFile(path.join(__dirname, ‘badge-template.svg‘), { 
    // ...
    logo,
    // ...
  });
  // ...
});

Now you can include a logo by adding a logo query parameter with the URL of the logo image:

http://localhost:3000/hello/world?logo=https://cdn.jsdelivr.net/npm/[email protected]/icons/github.svg

Deploying the Badge Generator

To make your badge generator publicly accessible, you‘ll need to deploy it to a hosting service. Some popular options include:

The deployment process will vary depending on the service you choose, but generally it involves:

  1. Creating an account with the hosting service
  2. Installing the hosting service‘s CLI tool
  3. Initializing a new project
  4. Configuring the project‘s settings (e.g. Node.js version, environment variables)
  5. Deploying the code to the hosting service

Refer to the documentation for your chosen hosting service for specific instructions.

Additional Considerations

Here are some additional topics to consider when building a production-ready badge generator:

Error Handling

Make sure to include proper error handling in your Express server. This includes handling invalid requests, missing parameters, and unexpected errors.

You can use Express middleware like express-validator to validate and sanitize incoming requests. And be sure to include catch-all error handling middleware at the end of your middleware chain to handle any uncaught errors.

Logging

It‘s a good idea to include logging in your Express server to help with debugging and monitoring. Popular logging libraries for Node.js include Winston and Bunyan.

Make sure to log incoming requests, outgoing responses, and any errors that occur. You can also log additional metadata like request duration, user agent, and more.

Security

Security is critical for any web application. Some key considerations for a badge generator include:

  • Using HTTPS to encrypt traffic between the client and server
  • Validating and sanitizing all incoming request data to prevent injection attacks
  • Setting appropriate HTTP headers like X-Content-Type-Options and X-XSS-Protection to prevent content sniffing and cross-site scripting attacks
  • Using a secure reverse proxy like Nginx or Caddy in front of your Express server
  • Keeping your dependencies up to date and monitoring for security vulnerabilities using tools like Snyk or npm audit

Caching

Caching can significantly improve the performance of your badge generator by reducing the number of requests that need to be processed by your server.

In addition to the Cache-Control header we added earlier, you can also use a caching service like Redis or Memcached to cache rendered badges. This can be especially helpful if you have a high volume of requests for a small set of badges.

You can also use a content delivery network (CDN) like Cloudflare or Fastly to cache and serve badges from locations closer to your users, reducing latency.

Conclusion

In this guide, we‘ve walked through the process of building a custom ReadMe badge generator using Node.js and Express. We covered:

  • Setting up a basic Express server
  • Creating an SVG template for the badge using Squirrelly
  • Handling badge generation requests and customizing badge appearance
  • Deploying the badge generator to a hosting service
  • Additional considerations like error handling, logging, security, and caching

With this knowledge, you should be well-equipped to build your own badge generator and start adding some flair to your project‘s ReadMe. Happy badging!

Similar Posts