Generating badges / shields with NodeJS

In the last post, I wrote a simple example to be able to generate SVGs through RaphaelJS on the server.

Now I would like to showcase how to accomplish a similar task – generating badges – again with a simple NodeJS server.

The approach

Shields are made of 3 main parts, a text on the left, a text on the right and the background color of the latter.

That said, it’s pretty clear we will want to receive those parameters in the HTTP request to the server and generate an SVG accordingly: we will simply use a base template and generate the image on the fly.

For the templating part we will use swig, and to simplify the process of extracting request parameters we will simply rely on the evergreen express.

Show me the code!

First, let’s create a package json so that we can npm install the required dependencies:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "name": "nodejs-badges",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "express": "^4.12.3",
    "swig": "^1.4.2"
  }
}

Now, down to the “real” code : let’s start by simply creating a brand new express app that receives requests at /badge/:left/:right/:color and renders an SVG template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var express = require('express')
var app = express()
var swig = require('swig')
var path = require('path')

app.get('/badge/:left/:right/:color', function (req, res) {
  var badge = swig.renderFile(path.join(__dirname, 'badge.svg'), req.params);

  res.writeHead(200, {"Content-Type": "image/svg+xml"})
  res.write(badge);
  res.end();
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Badge generator listening at http://%s:%s', host, port);
});

At this point, we will need to create the SVG template, and we will take inspiration from shields.io’s one, which is quite battle-tested:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

{% set leftWidth = left.length * 10 %}
{% set rightWidth = right.length * 12.5 %}
{% set totalWidth = leftWidth + rightWidth - 10 %}

<svg xmlns="http://www.w3.org/2000/svg" width="{{ totalWidth }}" height="18">
  <linearGradient id="smooth" x2="0" y2="100%">
    <stop offset="0"  stop-color="#fff" stop-opacity=".7"/>
    <stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
    <stop offset=".9" stop-color="#000" stop-opacity=".3"/>
    <stop offset="1"  stop-color="#000" stop-opacity=".5"/>
  </linearGradient>

  <mask id="round">
    <rect width="{{ totalWidth }}" height="18" rx="4" fill="#fff"/>
  </mask>

  <g mask="url(#round)">
    <rect width="{{ leftWidth }}" height="18" fill="#555"/>
    <rect x="{{ leftWidth }}" width="{{ rightWidth }}" height="18" fill="{{ color }}"/>
    <rect width="{{ totalWidth }}" height="18" fill="url(#smooth)"/>
  </g>

  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="12">
    <text x="{{ leftWidth /2+1 }}" y="14" fill="#010101" fill-opacity=".3">{{ left }}</text>
    <text x="{{ leftWidth /2+1 }}" y="13">{{ left }}</text>
    <text x="{{ leftWidth + rightWidth /2.5-1 }}" y="14" fill="#010101" fill-opacity=".3">{{ right }}</text>
    <text x="{{ leftWidth + rightWidth /2.5-1 }}" y="13">{{ right }}</text>
  </g>
</svg>

At this point we’re set; we can just run node index.js and hit localhost:3000/my%20 badge/is%20great/green to see the generated badge appear:

MOAR!

Keeping in mind that you have to URLescape special characters, you can now play around with many different combinations:

and so on and so fort: you can definitely feel free to customize the code to add some more variables, tweak the template and make small adjustments.

All in all, now I guess you see how these shields are generated :)

For the lazy ones…

Since I wanted to have a working example that people could run, rather than simply copypasting code from this post around, I created a simple github repo for nodejs-badges so that you can play around with more ease.

comments powered by Disqus