Building A Redirect Service with Cosmos DB and Azure Functions
Like I said in my last post, my blog is now a static site. And there’s some pretty awesome advantages to that. But not all aspects of static sites are unicorns and rainbows - there are a few drawbacks, one of which is that static sites can't do any computational work because they're simply HTML files.
In Wordpress, I used a plugin called Redirection, which enabled me to create URLs that would redirect to other websites. They can come in handy, especially when presenting. I'll often create a custom redirect link for each presentation I give that will point my attendees toward my resources for that topic. For example, if I were giving a talk about isolation levels, I might direct attendees to visit https://sqlbob.com/isolation, which would redirect them to my GitHub repo with links to slides, demo code, and other materials.
But a HTML file really can't redirect you to another page. To be fair, you can do it via a
Like any good project I started off with requirements gathering. I wanted to create an application that could:
- Create URLs that would redirect users elsewhere
- Return proper HTTP redirect codes when doing so
- Count how many times a redirect URL was visited
- Keep track of the last time a redirect URL was used
- Be able to add/remove redirect URLs easily
- Be able to disable redirect URLs without deleting them
- Utilize serverless technologies if possible. Since this static site, by definition, has no server, I really don't want to have to run one 24/7 just to power this service.
To power the above, I need a compute layer since a static site has none. In this case its objective is pretty simple. It needs to:
- Take in a parameter
- Look that parameter up and retrieve a redirect URL
- Return the redirect URL so the client is redirected
- Increment a counter to track that the redirect took place
Given my above desire to utilize a serverless technology, my choice was essentially between Azure Functions and AWS Lambda. Both are extremely strong and proven contenders which I am sure would serve my rather basic needs. Since this blog focuses on Microsoft technologies, I opted to implement this in Azure Functions.
In addition to compute, I also needed somewhere to store my data. Once again I was looking for a serverless offering because I'd rather not pay for a database service to run 24/7 just in case someone clicks a redirect link. For this, I had lots of options:
Azure SQL Database
Azure SQL Database would work just fine, though like I just mentioned, I'd be paying for a service to run constantly, most of the time when it wasn't necessary. There's also the serverless option available, which is much more attractive because it only runs when necessary. From a pricing perspective this is awesome. In terms of performance, though, it's important to remember that if a serverless resource is not running, the first query sent to it will require it to start, which may take a few seconds.
Azure SQL Managed Instance
Azure SQL Managed Instance would absolutely work as well, though in this case it's overkill as I only need basic database capabilities. No need to use a cannon to kill a mosquito for this project.
Azure Cosmos DB
While I could very easily implement what I need in a relational database, I could go the non-relational route as well. And Cosmos DB's multi-model architecture gives me several choices - documents would work. Tables would work as well. Like Azure SQL Database, Cosmos DB has a serverless option as well so I wouldn't need to have a resource running all the time.
Azure Table Storage
This is a technology that's part of Azure Storage accounts, and is not to be confused with "Azure Cosmos DB for Table", which I mentioned above. Azure Table Storage is basically an API into an Azure Storage account, which lets you make use of a key-value store. It's serverless and very inexpensive to use. Azure Cosmos DB for Table offers additional features and SLAs in line with what Cosmos DB provides in general, however if you don't need them this is a very strong contender.
While I very easily could have implemented a solution with a relational database option, and Azure Table Storage seemed very tempting, I ended up choosing Azure Cosmos DB and using the document model. I like the flexibility that documents offer, and they have proven to be perfect for my use case.
A very important question to ask whenever building something in the cloud is the cost. For Azure Functions I am using the consumption plan, so it's essentially pay-as-you-go. But it still shouldn't cost me much because that model includes a monthly free grant of 1 million executions and 400K GB-s of resource consumption. I doubt I'll exceed those numbers, so it's likely that my Azure Function will cost me nothing. Creation of an Azure Functions app also requires a storage account, which is not included in the free grant. The storage account is billed at regular storage rates and networking rates. But my app is tiny and relatively infrequently-accessed. I've yet to be billed 1 cent for this usage either.
Then for the database. I've blogged about this topic previously, but Cosmos DB has a free tier which is free forever and includes 25GB of storage and 1000 RU/s. This is more than enough capacity to handle my application's needs. The free tier is not currently available for serverless accounts, so I do have a Cosmos DB database running continuously to service this application, but it's not costing me anything so I'm ok with that.
So yes, it could cost me money to operate this redirection service, but so far it has not, and thanks to the generosity of the Azure Functions free grant and Cosmos DB free tier, I don't think it ever will unless my number of redirect requests increases by several orders of magnitude.
If you would like to try this yourself, here is the code I used. I have included an annotated version of the Azure Function as well as an example of what a Cosmos DB document looks like.