Serverless URL Shortener
Building a URL shortener is much easier now using the Serverless technologies in Azure. In this post, we will discuss developing this solution.
Here is the overall architecture of the solution.
These are the priorities I set for this solution:
- Use Serverless component, which inherently makes it scalable & resilient.
- Make sure it is secure.
- Optimize it for cost.
- Use Infrastructure as code.
To create a short URL, we need to generate a unique hash. Azure Cosmos DB for NoSQL is the most suitable datastore for storing the hash, original URL, and additional required data. We will use Azure Functions consumption plan to generate the short URLs triggered by the events to generate the URL. Azure API Management is the logical choice for APIs, for this use case consumption tier will be suitable. We need a management console to view the existing URLs of the user which can be hosted as a Single Page Application (SPA) on Azure Blob Storage. We will use Azure Front Door to deliver the SPA and API to the end users. Authentication to the Management Console will be supported by Azure AD B2C.
User Authentication:
In the Azure AD B2C, create a User Flow of type “Sign up and sign in”. In the user flow, for user attributes include Email Address, Given Name, and Surname and for application claims include Email Addresses, Given Name, Surname, and User’s Object ID attributes. Register two app clients, one for SPA and another for API. In API app client, add two scopes API Write & API Read under Expose an API setting. In SPA app client, under API permissions add the two scopes that we created in the API app client. For step-by-step instructions on configuring Azure AD B2C, check this documentation. Note down the name of the Azure AD B2C tenant and User Flow, Client ID of API app client and SPA app client, and fully qualified URL of the API Write & API Read scopes as we will need them while deploying the solution.
Scalable Frontend:
I am using Azure Front Door as the CDN with only HTTPS enabled and using TLS 1.2. Using Rule set, rules are configured to route the request to the APIs hosted in API Management or SPA hosted in Storage Blob. Azure Storage Blob origin is connected to Front Door through Private Link hence it won’t be accessible from network outside of it. To secure the API endpoint, API Management policy is configured to validate that the request contains the HTTP Header X-Azure-FDID
which is set to the ID of our Front Door instance. This makes sure that the APIs will accept requests only from our own Front Door and nowhere else.
Serverless Backend:
Datastore: We will create a Serverless Cosmos DB account with zone redundancy to support resiliency over zonal failures. We will create a Cosmos DB container with /id
as the partition key. Unique hash that will be generated for every URL will be stored as the value of id which follows the Cosmos DB best practice of having high cardinal value for partition key.
APIs: We need at least 4 APIs, create short URL, get the original URL from short URL, list all the URLs specific to a user, and delete a specific URL. API Management supports validating the JWT token generated by Azure AD B2C after the user authentication.
API Management policy also supports making direct REST API call to Cosmos DB service and we will directly access Cosmos DB to get the original URL and Delete an URL. Using the authentication-managed-identity
policy we will authenticate with the backend service (in our case Cosmos DB). Update the backend service to Cosmos DB endpoint using the set-backend-service
policy. Cosmos DB REST API call requires certain headers (e.g., Authorization, x-ms-date, x-ms-version, and x-ms-documentdb-partitionkey) which can be set using set-header
policy. Values for a few of these headers (e.g., Authorization, x-ms-documentdb-partitionkey) need to be formulated by us and we will use policy expressions to generate or obtain them. Cosmos DB REST API call should target the specific URI, we will use rewrite-uri
policy to point to the expected URI.
Compute: We need Azure Function to create the short URL where we need to generate unique hash for the short URL and list URLs as we need to verify whether the original URL is still accessible. I have written a short python-based Azure Function to generate a hash, validate that it’s unique among the existing data. I have added the original URL and the object ID of the user (based on the value in JWT token) who created the short URL. Similarly, another Azure Function is used to get the list of short URLs created by specific user and validate whether that URL is still accessible to detect broken links. These two functions are deployed in a single Function App.
This is how the management console looks like:
Code Example:
Example implementation of this solution is available in Serverless URL Shortener on Azure GitHub repository.
This repository contains the following:
- Single Page Application (SPA) [HTML/JavaScript]
- Azure Function code to create the short URL [Python]
- Deployment script which enables you to deploy the complete solution [Python]
- Example configuration file used by the deployment script [YAML]
- Bicep template to create the required infrastructure [Bicep]
Here is a quick summary of forecasted cost per month in Azure West Europe (Amsterdam) region.
Service |
Price Dimension (per month) |
Cost |
Storage Account |
Blobs (1GB) |
0.32 |
API Management |
1MM requests |
0 |
Function |
100K Executions |
0 |
Cosmos DB |
1MM requests & 1GB storage |
0.31 |
Azure Front Door |
Premium 5GB data transfer |
330.51** |
Application Insights |
Ingestion & |
3.0 |
Total |
334.14 |
** I am using Azure Front Door Premium subscription to connect to Azure Storage Account through Private Link. In this case, I have over indexed on security than cost. Exposing storage blobs to public internet is not safe and in addition if people have direct access to the blobs, they can make requests to the blobs directly and drive the cost. It is possible to use Azure Front Door Standard for $35/month and secure the blob storage by allowing access only to the Front Door IP range. But I would highly recommend using Private Link for production use cases.
Published on:
Learn more