Blog Post

Apps on Azure Blog
5 MIN READ

Serverless URL Shortener

ppraka5h's avatar
ppraka5h
Icon for Microsoft rankMicrosoft
Feb 27, 2023

Building a URL shortener is much easier now using the Serverless technologies in Azure. In this post, we will discuss developing this solution.

 

Update: My colleague (Christopher Maneu) pointed out that we could optimize the cost by using Azure Static Web Apps (SWA) for hosting the management console. I updated the design to use SWA and added another API to check what the target URL is for the specific short URL gets resolved without visiting the site.

 

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 Static Web Apps. 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 SWA. 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.

Link Checker API: This API lets you check what target URL does a particular short URL resolve to. You might want to check whether a URL is legit before visiting it and this is an extremely useful capability.

 

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:

Link Checker UI:

 

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

Static Web Apps

Free

0

API Management

1MM requests

0

Function

100K Executions

0

Cosmos DB

1MM requests & 1GB storage

0.31

Azure Front Door

Standard 5GB data transfer

35**

Application Insights

Ingestion & Storage

3.0

 

Total

38.63

 

** Initial design used Azure Front Door Premium, whereas while using Azure Static Web Apps we don't need Premium subscription, hence using Standard. I could have used only Static Web Apps (like this solution) with Functions as API backend. But I decided to use API Management as it has robust capabilities and just to perform the URL redirect using Functions is not cost effective. With Front Door it caches most of the requests hence offers better performance and reduced cost when we scale.

Updated Mar 20, 2023
Version 3.0
  • jfmatth's avatar
    jfmatth
    Copper Contributor

    ppraka5h thank you so much for making those changes.  

     

    I've always wanted to build a URL shortener for edification sake, and use Azure, but the cost was too high, not now!

     

    Thanks again.  Keep up the good work

  • jfmatth's avatar
    jfmatth
    Copper Contributor

    Great article, thank you.

     

    So, what is the cost w/o FrontDoor?  Just 4.14/mo?

     

     

  • jfmatth Thank you!

    I updated the design now to use Azure Static Web Apps for Single Page Application and Azure Front Door Standard instead of Premium. Similarly, you can skip Front Door and use Static Web Apps directly and point your API directly to API Managment Endpoint to save cost. Which will effectively be less than 5/month based on the number of requests that I calculated above.

  • Very Interesting! You could do an interesting consolidated architecture with Azure Container Apps + YARP, which could potentially replace Front Door, APIM, SWA & Functions.

  • HowardvanRooijen Yes, Azure Container Apps is a viable alternative to consolidate. At the same time, we need to keep running at least one Container Apps and scale significantly to handle the load in case of big growth. Most of the requests to URL shortener are GET requests and with Front Door that can be cached and reduce the number of backend calls. APIM also handles JWT authentication and managed identity in this case without any additional code whereas with Container Apps & YARP we need to write additional code and manage it.