Building a URL Shortener with Azure Functions in less than 100 lines of code

07/05/2017

Today’s post describes how Azure Function’s very easily can be used to build real world applications. I build the basis application on my train ride home from Azure Global Bootcamp, an hour from idea to working implementation.

The application is a URL shortener, like you know from tinyurl, bit.ly or similar service. It simply takes any URL and turns it into a short URL you can use in your twitter posts or whenever. I know twitter have their own url shortening service, but with your own service you get a lot of flexibility, to name a few.

Let’s see how to build it. The service consists of two functions and a proxy for url rewrite.

Create function

A function called create that allows creation of new short urls. In my implementation, I support autogenerating a 7 character short url and also letting the user supply it. The mapping from short url to url is saved in an Azure table called ShortKeys, using the function binding for doing so, which means very few lines of code needed to do the it. The short code is used as the primary key of the table, and I just added a dummy static value “A” as the row key, while placing the url in a column specifically for that purpose.

I have not build a fancy UI I simply have an API end point that I can call with /api/create?url=http://mylongurl.com or /api/create?url=http://mylongurl.com&shortcode=ml

The code looks like this
[csharp]
#load "..\models.csx"

using System.Net;

public static HttpResponseMessage Run(HttpRequestMessage req, ICollector<ShortKey> shortKeys, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");

// parse query parameter
var qs = req.GetQueryNameValuePairs();
string url = qs.FirstOrDefault(q => string.Compare(q.Key, "Url", true) == 0).Value;

var id = qs.FirstOrDefault(q => string.Compare(q.Key, "ShortCode", true) == 0).Value ?? ShortCode.NewShortCodeByDate(DateTime.UtcNow);
if (url != null)
{
shortKeys.Add(new ShortKey()
{
PartitionKey = id,
RowKey = "A",
Url = url
});
}

return url == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a Url on the query string")
: req.CreateResponse(HttpStatusCode.OK, "Your short key is: " + id);
}
[/csharp]

You can view the code for models.csx and shortcode.csx in the github repository that contains the entire code base: https://github.com/sjkp/urlshortener

Redirect function

The other function is responsible for redirecting the visitor to the right url from the short url. This is again done using mostly Azure Bindings. As part of the function I also track some metrics about the requester, as it is always interesting to see who uses your short urls. The tracking data is stored in another Azure Table called Analytics.

[csharp]
#load "..\models.csx"

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ShortKey shortKeys, string shortCode, ICollector<Analytic> analytics, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
analytics.Add(new Analytic()
{
PartitionKey = shortCode,
RowKey = Guid.NewGuid().ToString(),
Url = shortKeys.Url,
UserAgent = string.Join(",", req.Headers.GetValues("User-Agent")),
ClientIp = string.Join(",", req.Headers.GetValues("X-Forwarded-For"))
});
var res = req.CreateResponse(HttpStatusCode.Redirect);
res.Headers.Add("Location", shortKeys.Url);
return res;
}
[/csharp]

Proxy and domain setup

Since we don’t want our visitors to use a long url for the short url like https://sjkpshorturl.azurewebsites.net/api/redirect?shortcode=ml we need to do a few things to allow our function to be a real URL shortener. First thing is to get a short domain name and bind it to the azurewebsites.net domain. I just used a subdomain of my own personal domain, so my service is available at u.sjkp.dk. Next we would like to just be able to write /shortcode. For that we can use the proxy functionality of azure function, to set up a route that redirects requests to /{*shortcode} to https://sjkpshorturl.azurewebsites.net/api/redirect?shortcode={shortcode}. Luckily that is exactly the purpose of the proxy functionality.

My proxies.json looks like this
[js]
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"RedirectUrl": {
"matchCondition": {
"route": "/{*shortCode}",
"methods": [
"GET"
]
},
"backendUri": "https://%WEBSITE_HOSTNAME%/api/redirect/{shortCode}"
},
"Api": {
"matchCondition": {
"route": "/api/{*path}"
},
"backendUri": "https://%WEBSITE_HOSTNAME%/api/{path}"
}
}
}
[/js]

Summary

That is all it took to build a URL shortener services.

If you are interested in using the solution, feel free to take it from https://github.com/sjkp/urlshortener or use the shorturl http://u.sjkp.dk/shorturl. If you have issues just open them as issues in github, and I will try to help out.