Build your own: URL shortener

URL shorteners are a common service to convert a long URL in a more comprehensible shorter URL. There are a number of free-to-use shorteners available, but those bring in privacy concerns and don’t always support the features required by the business.

Deep links are usually created in a Mendix app via the deep link module. URLs generated by this module are often quite long (for example https://mymendixapp.stephanbruijnis.dev/link/DeepLinkName?requestParameter=8027e5c7-0fc6-cb80-4a58-e9779249f5a0&moreInfo=ABC123). Creating a shortened URL allows for easier sharing and reading of the URL. This how-to will describe the steps to build your own URL shortener service.

Functions

There are two functions in this service

  • url-shortener service: a long link is provided as input and a shortened variant of that URL is returned as response
  • redirect service: the shortened URL is accessed and the user gets redirected to the long URL

Basic setup

The url-shortener service allows you to request a shortened variant of a URL. The process can be initiated by another app or module within the same Mendix app by sending a request message containing the long URL to the service. The url-shortener service will then generate a shortened variant of the long URL and respond with a messaging containing the shortened version. If desired, the requestor can provide requested_validity_seconds which indicates the short URL is only valid for a certain amount of time, expressed in seconds. The url-shortener will persist the mapping between the long-url and the shortened version as well as a ValidUntil attribute.

The redirect service allows you to redirect users (anyone who accesses the shortened link) to the right target: the long URL. The browser of the user will do a GET request to the shortened URL and the service will respond with a 301

Concept

Building the services

url-shortener

  1. Create a non persistent entity with two attributes: longUrl and requestedValiditySeconds(optional)
  2. Create a persistent entity with the attributes LongUrl (string 2048), ShortURL (string 2048), ValidUntil (dateTime) and add a unique validation rule on the ShortURL1
  3. Right click entity; Expose as REST resource
  4. Select POST method
  5. Change the microflow to perform the following actions:
    1. Validate the request according to your business rules (is the long URL not empty, for an allowed domain etc.),
    2. Generate a random string2 which will be used to create the short URL
    3. Create a new object that stores the relationship between the long URL and short URL in the previously created persistent entity3 and set the ValidUntil based on the currentDateTime + requestedValiditySeconds
    4. Return the short URL as a response message
  6. Add authentication to secure the published REST service (basic authentication, custom)

Microflow for POST method

redirect

  1. Create new REST service, give the location a short name (e.g. url)
  2. Add a resource with a short name (e.g. s) GET method
  3. Add new microflow, set Operation path to {tag} and add a Parameter called tag of type Path
  4. Change the microflow to perform the following actions:
    1. Validate the tag / requested URL
    2. Retrieve the RegisteredURL (mapping ShortUrl to LongUrl) from database
    3. Return 301 together with a response header (Location: $RegisteredURL/LongUrl) or a 404 when no value is found

Microflow for GET Redirect

result

POST a request to the url-shortener service with the following body (which contains an example of a very long url):

{
  "longUrl": "http://chart.apis.google.com/chart?chs=500x500&chma=0,0,100,100&cht=p&chco=FF0000%2CFFFF00%7CFF8000%2C00FF00%7C00FF00%2C0000FF&chd=t%3A122%2C42%2C17%2C10%2C8%2C7%2C7%2C7%2C7%2C6%2C6%2C6%2C6%2C5%2C5&chl=122%7C42%7C17%7C10%7C8%7C7%7C7%7C7%7C7%7C6%7C6%7C6%7C6%7C5%7C5&chdl=android%7Cjava%7Cstack-trace%7Cbroadcastreceiver%7Candroid-ndk%7Cuser-agent%7Candroid-webview%7Cwebview%7Cbackground%7Cmultithreading%7Candroid-source%7Csms%7Cadb%7Csollections%7Cactivity|Chart",
  "requestedValiditySeconds": 600
}

The response will look something like this, and will be received by the consuming app. The shortened URL can now be used in notifications, messages, etc.

{"shortenedUrl": "http://localhost:8080/url/s/d766"}

Access the URL http://localhost:8080/url/s/d766 in the browser and open the browser’s developer tools (F12) on the Network tab. It will show the request and the 301 response.

Network request with 301 status

dependencies

This logic depends on:

  • CommunityCommons.GetApplicationUrl (java action)
  • CommunityCommons.RandomString (java action)
  • REST_Responses (various java actions that generate the http responses)

Performance

You may have noticed that this redirect is blazing fast compared to loading a Mendix page. And yet, it’s still Mendix: the browser does a GET request to a Mendix REST service instead of a Mendix page. Which means the entire Mendix client (mxui.js, javascript, pages, css, images etc.) isn’t loaded. It’s a single network request to the Mendix app which triggers the microflow defined in the redirect service, which is optimized to give a fast response and load nothing more than absolutely required.

Security

There are a few security considerations to this implementation.

  • Guessing of links: to prevent bad actors from easily guessing the links the short link needs to be (1) of sufficient length and (2) only use about 10% of the possible permutations4
  • Unauthorized access: the shortened version of the link adds no additional security, make sure you add the appropriate access on the target (e.g. the Mendix deep link handling)
  • Published REST services: the GET method for the redirect needs to be accessible for anonymous users, however the url-shortener should require authorization. That’s why they should be build as two separate services, so different security can be applied

  1. this has a number of advantages; it gets indexed in the database which will improve the performance of read operations (retrieve) as well as enforce data consistency: a shortened URL can only be registered once. ↩︎

  2. the random string can be alphanumeric (a-z0-9), or just numeric (0-9) or just alphabetical (a-z). Depending on what you pick alphanumeric offers 36 different characters per position, numeric only 10 and alphabetic 26. ↩︎

  3. since we enforce unique values for the shortUrl attribute, it might cause an exception when an object is created with a shortUrl that already exists, changes are very small but this exception should be handled via an error handler (and try again with a new shortUrl) ↩︎

  4. a shortened url consisting of 4 alphanumeric characters has 1.4 million permutations (using 10% would mean generating 140.000 unique links and still have 90% unused, making guessing quite hard). When using 5 alphanumeric characters yields 45.2 million permutations (using 10% would mean 4.52 million unique links, sufficient for most Mendix apps =) Note: that the number of permutations is lower when only alphabetical or only numeric characters are used. ↩︎