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 responseredirect
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
Building the services
url-shortener
- Create a non persistent entity with two attributes:
longUrl
andrequestedValiditySeconds
(optional) - Create a persistent entity with the attributes
LongUrl (string 2048)
,ShortURL (string 2048)
,ValidUntil (dateTime)
and add a unique validation rule on theShortURL
1 - Right click entity; Expose as REST resource
- Select POST method
- Change the microflow to perform the following actions:
- Validate the request according to your business rules (is the long URL not empty, for an allowed domain etc.),
- Generate a random string2 which will be used to create the short URL
- 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
- Return the short URL as a response message
- Add authentication to secure the published REST service (basic authentication, custom)
redirect
- Create new REST service, give the location a short name (e.g.
url
) - Add a resource with a short name (e.g.
s
) GET method - Add new microflow, set Operation path to
{tag}
and add a Parameter calledtag
of type Path - Change the microflow to perform the following actions:
- Validate the tag / requested URL
- Retrieve the RegisteredURL (mapping ShortUrl to LongUrl) from database
- Return 301 together with a response header (
Location: $RegisteredURL/LongUrl
) or a 404 when no value is found
result
POST a request to the url-shortener
service with the following body (which contains an example of a very long url):
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.
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.
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
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. ↩︎
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. ↩︎
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) ↩︎
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. ↩︎