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, ...