How I do Twitter Cards

As you may have noticed, the client side of my blog is AngularJS

As you may have noticed, the client side of my blog is AngularJS. This means that it doesn't render individual pages like a static site does. In order for Twitter's Cards to work, the response returned from the site when Twitterbot crawls it must contain the necessary metatags, like:

<html>
  <head>
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:site" content="@beamjack" />
    <meta name="twitter:title" content="An Article About Stuff" />
    <meta name="twitter:image" content="http://4d4ms.com/img/a.jpg" />
    <meta name="twitter:description" content="Some descriptive text of the article" />
  </head>
</html>

But a typical response if you go to a blog article like http://www.4d4ms.com/blog/57d1805038b5710a02e9d894 will return the same basic response no matter what article the url indicates (and then load the article asynchronously).

So how do we specialize the metatags in order for a card tailored to the individual article to be made? We return a different response to Twitterbot! Compojure makes this really easy. If a route we define does not return a response object, then another matching route will get a chance to handle the request.

  ;; JSON payload for an article e.g. /blog/articles/1234.json
  (GET "/blog/articles/:id.json" {user :user {:keys [id]} :params}
       (let [article (db/blog-article id)]
       ...
  ;; Twitterbot specific route for an article e.g. /blog/1234
  (GET "/blog/:id" {user :user {:keys [id]} :params {:strs [user-agent]} :headers}
       (when (clojure.string/includes? user-agent "Twitterbot")
       ...
  ;; all other requests get redirected to index
  (rfn ...)

Note that I have two different routes for an article. The /blog/articles/123.json route is an internal url that the AngularJS application hits to get a blog article's payload, the /blog/123 route is the url shared when I share a link to my blog. If Twitterbot hits that url, I serve a special templated response for the article:

{{=<% %>=}}\n<html>\n  <head>\n    <meta name="twitter:card" content="<% card %>" />
    <meta name="twitter:site" content="<% site %>" />
    <meta name="twitter:title" content="<% title %>" />
    <meta name="twitter:image" content="<% image %>" />
    <meta name="twitter:description" content="<% description %>" />
  </head>
</html>

Then I just fill in the mustache variables, using Stencil, and voila

$ curl -A Twitterbot http://www.4d4ms.com/blog/57d1805038b5710a02e9d894
<html>
  <head>
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:site" content="@beamjack" />
    <meta name="twitter:title" content="How I do Twitter Cards" />
    <meta name="twitter:image" content="http://4d4ms.com/img/A.jpg" />
    <meta name="twitter:description" content="<p>;As you may have noticed, ...