Advent Calendar 2025 – De-/Activate Mappings – Part 1

Why an active/inactive model for shortlinks?

For many users – especially those who work in the field of software development – shortlinks are much more than simple URL shorteners. They act as flexible routing mechanisms for campaigns, feature controls, test scenarios, and internal tools. The requirements for transparency, controllability and clean lifecycle management are correspondingly high.

The URL shortener receives an active/inactive model that directly supports these claims. A user can now specify, for each shortlink, whether it should be active, temporarily deactivated, or no longer allowed to reach users due to a planned expiration. At the same time, a clear distinction is made between a disabled and an expired shortlink – both in the UI and in the HTTP behaviour.

For the user, this creates a noticeable gain in control. An entire campaign can be taken offline with a single click, without losing data or having to edit multiple entries manually. The REST API or the Java client can be used to automatically control activity status externally, enabling integrations with existing development and deployment processes.

This introduction outlines the motivation for the new model. The following chapters detail how the UI, API, data model, and redirect logic have been extended, and how users benefit from the new active/inactive mechanism.

The source code for this project‘s status can be found on GitHub at the following URL: https://github.com/svenruppert/url-shortener/tree/feature/advent-2025-day-10

Overview of a URL shortener interface displaying shortcodes, URLs, creation dates, activity status, expiration dates, and action buttons.
Screenshot of a URL shortener overview interface displaying a table with shortcode, URL, created date, activity status, expiration, and action buttons.

Architecture overview of changes

The new active/inactive model does not work in isolation within a single module; it spans multiple layers of the application. This creates consistent behaviour for the user – regardless of whether a shortlink is edited via the UI, automatically managed via the API or accessed directly in the browser. To understand this interaction, it is worth examining the key areas where adjustments have been made.

The basis is the extended data model, which now includes an additional attribute to store the activity status. This feature serves as the common denominator for all shortlink-related operations. As a result, both the REST endpoints and the internal Java client have been extended to reliably transmit activity status when creating, modifying, or querying a shortlink.

These structural changes are also reflected in the user interface. The activity status can now be set directly when creating a shortlink, in the overview table, or in the detail dialogue. Actions such as activating or deactivating multiple entries simultaneously use the exact mechanisms as the API, creating a consistent application experience.

Last but not least, the redirect behaviour has also been adjusted. While expired shortlinks are clearly indicated, deactivated shortlinks are not. In this way, the user can not only better understand why a shortlink is unreachable, but also receive more precise feedback from both client and monitoring perspectives.

This architectural overview shows that the active/inactive model is not a one-off feature, but a consistent design principle across all layers of the system. In the following chapters, the respective areas are considered in detail and technically explained.

The Advanced Data Model

Before the active/inactive model becomes visible in the user interface, REST API, or redirect behaviour, the basic structure must be defined in the data model. Chapter 3 focuses on this basis and shows how a shortlink’s activity status is anchored in the core of the system.

The data model is the central place where all relevant information converges into a shortlink. Every user action – be it creating, editing, filtering or forwarding a shortlink – sooner or later falls back on this structure. Therefore, it is crucial to introduce an activity status that is consistently and reliably available.

This chapter explains how the new active attribute is mapped to domain objects, how it is transported in DTOs, and the serialisation and backwards-compatibility considerations involved. This clean anchoring in the data model makes the active/inactive model a stable part of the entire application, on which all further technical enhancements are based.

New active flag in the core domain model.

The introduction of the active/inactive model begins at the core domain model level. This defines how a shortlink stores its state and what information is transmitted within the application. The new active attribute lets users check at any time whether a shortlink is currently in use or has been deliberately deactivated.

The central element of this change is the extension of the ShortUrlMapping class. It outlines the essential characteristics of a short link, including the alias, the original destination URL, and the expiration dates. The new attribute enables the unique determination of the activity status within this structure.

In the original implementation, the relevant snippet looks like this:

This enhancement ensures that activity status is reliably available both in memory and in all downstream layers of the system. The model remains unchanged. The status is not changed in an existing object; instead, it is created as a new instance using the withActive method. This prevents unintended side effects and supports consistent, deterministic data behaviour.

This clear separation between active and inactive states provides a stable foundation for UI, server, and redirect logic. In the following subchapters, we will discuss how this information is mapped to the DTOs and the consequences for serialisation and transport behaviour.

Impact on DTOs (ShortenRequest, ShortUrlMapping)

The active/inactive model not only affects the domain core but must also be transported cleanly across the application boundary. Data Transfer Objects (DTOs) are crucial for this. They serve as the interface between the user interface, REST API, and Java client, ensuring that the activity status of a shortlink is transmitted correctly.

With the expansion of the data model, the DTO ShortUrlMapping also receives an additional attribute that reflects the status of a shortlink. This value is used both in communication from the server to the UI and between the server and the client. A relevant excerpt from the original implementation looks like this:

This extended DTO enables the user to view activity status in the UI and allows external systems to read the status and respond accordingly. By using a record, structure and serialisation remain lean and clearly defined.

The request to create or change a shortlink has also been adjusted. The ShortenRequest now also includes information on whether a shortlink should be made directly active or initially deactivated. This allows the user to control how the shortlink behaves during creation.

An excerpt from the corresponding record:

At this point, it is evident that the request uses a Boolean, whereas the mapping itself uses a primitive Boolean. This difference is deliberate: the value in the request may also be null if the user does not want to set an explicit state. In this case, the server logic decides on a default value. Mapping, on the other hand, always has a defined state, eliminating ambiguity.

Extending DTOs ensures the active/inactive model is uniformly available across all system boundaries. It forms the basis for the UI, API and client to be informed identically about the status of a shortlink, thus creating a consistent user experience.

Serialisation and backward compatibility

For the active/inactive model to function reliably, the new active value must not only be present in the internal data model but also correctly serialised and transported across different components. The extension of serialisation affects two areas in particular: JSON communication via REST and data exchange between the various application modules.

By using Java records in the DTOs, serialisation is primarily handled by the chosen JSON library. Once the active attribute is defined in the record constructors, it is automatically included in the JSON representation. This makes integration straightforward and minimises the need for additional configuration.

An example of the resulting JSON structure of a shortlink as served via the REST API:

The JSON structure makes it clear that the activity state is immediately visible to the user and can be retrieved without any additional logic. This increase in transparency is central to the new model.

An essential aspect of this change is backward compatibility. Systems or components that use older versions of the API and are not aware of the active field can continue to interact without issues. JSON consumers typically ignore unknown fields to avoid affecting older clients. At the same time, the server can set a sensible default value for requests that do not specify an active value – typically true.

This combination of clear serialisation and high backward compatibility ensures that the active/inactive model can be integrated into existing systems without disruption. At the same time, further development remains open to future extensions, such as more differentiated activity states or audit information.

Enhancements to the Admin REST API

The active/inactive model only unfolds its full effect through the extensions of the administrative REST API. This establishes the connection between the data model, the internal business logic, and the various clients that create, edit, or automatically manage shortlinks. To enable users to effectively use the new activity state, several API endpoints had to be adapted or supplemented.

Chapter 4 examines these extensions in detail. It shows how the new toggle endpoint enables targeted switching of activity status, how inactive entries can be queried via a dedicated list endpoint, and how the ActiveState filter extends existing query operations. It also explains how differentiated HTTP status codes clearly convey the semantic meaning of a deactivated or expired shortlink.

These API customisations create a coherent, well-integrated interface that can map all aspects of the active/inactive model, whether the requests come from a user interface, a Java client, or an automated system.

New Toggle Endpoint

To make the active/inactive shortlink model usable across the application, the server component needs a well-defined mechanism to change the activity status of an existing shortlink. For this purpose, a new toggle endpoint was introduced and implemented on the server side by ToggleActiveHandler. It forms the basis for allowing users to adjust the activity state of a shortlink directly from the REST API – whether from the user interface, automations, or external systems.

The new endpoint has a clear purpose: it enables the targeted switching of the activity status of a specific shortlink. The user can specify, via a simple request, whether a shortlink should be activated or deactivated without changing any other mapping properties. This not only makes the operation more efficient, but also less error-prone, as there is no need for full update payloads.

This endpoint integrates seamlessly with the existing API structure and uses the same transport models and validation mechanisms as other administrative operations. At the same time, it provides a clear separation between changes of activity status and other editing operations, simplifying both implementation and use.

The core component of this endpoint is the ToggleActiveHandler. It encapsulates the HTTP-specific processing and delegates the actual state change to the UrlMappingStore behind it:

The handler implements the HttpHandler interface and integrates project-specific logging via HasLogger. In the constructor, the dependency is injected into the UrlMappingStore, which is later used to execute the logic that changes the state.

The main logic is in the try block. First, the request body is completely read in via readBody(ex.getRequestBody()) and converted to an instance of ToggleActiveRequest using fromJson. This Request object contains the two relevant pieces of information: the shortCode and the new active value. It then checks whether the shortcode is empty or null. In this case, the handler responds immediately with a 400 Bad Request and a clear error message, preventing the store layer from encountering invalid data.

If the shortcode is valid, the desired new activity value is extracted and logged. The actual status change occurs via “store.toggleActive(shortCode, newActiveValue)". The result is encapsulated in a Result<ToggleActiveResponse> to ensure both success and failure cases are handled consistently.

In case of success (mapping.isPresent()), another success entry is written to the log, and the updated display of the shortlink is returned to the user as JSON with the HTTP status 200 OK. If the store does not return a result, the cause of the error is logged via ifFailed, and the user is sent a response with a 400 Bad Request status code and a generic error message. This clearly defines the error behaviour without revealing too many details internally.

If unexpected runtime errors occur in the handler itself, the catch block catches the exception, logs a warning, and returns a 500 Internal Server Error. The finally block ensures that the connection is closed cleanly via ex.close() in all cases and that a corresponding log entry is created. Overall, this creates a robust, clearly structured endpoint that reliably and comprehensibly updates a shortlink‘s activity status.

Query inactive links

In addition to targeting the activity status of an individual shortlink, the user needs the ability to view collected inactive entries. This provides an overview of shortlinks that have been deliberately or automatically disabled and are currently no longer redirecting.

For this purpose, a special API endpoint was introduced that returns only inactive shortlinks. This endpoint complements the general list endpoint and provides a clear, filtered view of all shortlinks with the “inactive” status.

The focus of this endpoint is on visibility and control. Users can quickly see which shortlinks are currently disabled without having to implement their own filtering logic or evaluate the complete list of shortlinks. This is a notable advantage, especially in larger installations or automated workflows, as inactive entries often require separate management processes.

The endpoint integrates seamlessly with the existing administrative API structure and uses the same data models and return formats as other query operations. It ensures that information about inactive shortlinks is consistently retrievable across all UIs and clients.

The technical implementation of querying inactive shortlinks is integrated into the existing list handler. The following excerpt shows the ListHandler responsible for this, which provides different list variants, including the inactive entries, via a common endpoint:

The ListHandler, like the toggle handler, implements the HttpHandler interface and uses HasLogger for consistent logging. A UrlMappingLookup is injected into the constructor and used for the data queries. The handle method handles routing based on the request path: Depending on whether the request ends in PATH_ADMIN_LIST_ALL, PATH_ADMIN_LIST_ACTIVE, or PATH_ADMIN_LIST_INACTIVE, a specialised list method is called.

The listInActive() method is responsible for querying inactive shortlinks. It determines the current time and calls filterAndBuild("inactive", ...) with a predicate that selects the desired entries. A shortlink is considered inactive if it is either explicitly marked as inactive (!m.active()) or if it is active but has already expired. The combination of the active flag and the expiration date allows a clean separation between currently usable and no longer usable entries.

The filterAndBuild method reads all entries from the UrlMappingLookup, applies the passed predicate, and converts the remaining mappings into a DTO-like structure. For this purpose, toDto is used to create a map of the most essential attributes from each ShortUrlMapping. In addition to shortCode, originalUrl, createdAt, and expiresAt, the active status and a derived field, status, are set here, which distinguishes between expired and active. This additional information makes it easier for the user to read the entry status directly from the response.

This structure allows queries for inactive links to fit seamlessly into the existing list concept. Instead of introducing a completely independent endpoint, the existing infrastructure is used and expanded to include a targeted view of inactive entries. The result is a precise, reusable mechanism that always gives users a complete overview of all currently inactive shortlinks.

Adjustments in the list request (ActiveState filter)

To allow users to search specifically for active, inactive, or all shortlinks, a dedicated activity status filter has been added to the existing list mechanism. This so-called ActiveState filter complements the previous filter criteria, such as text search, sorting, time periods, or pagination, and enables precise control of the search results via the API.

This extension provides the user with a flexible yet clearly defined way to set the desired status directly via a query parameter. Instead of retrieving the entire list of shortlinks and then filtering it on the client side, the request can now be restricted directly on the server. This saves resources, reduces unnecessary data transfers, and ensures consistent search results.

The ActiveState filter considers three possible states:

  • Active – The shortlink is active and, if there is an expiration date, has not yet expired.
  • Inactive – The shortlink has been disabled by the user or has expired.
  • Not set – The user does not provide a status indication, so the filter is not applied, and all relevant entries are considered.

This distinction is communicated via the active query parameter, which is passed to the standard list endpoint in a GET request. The server evaluates this parameter and creates a corresponding UrlMappingFilter object based on it, which controls entry selection.

The technical implementation of this filter logic is handled in the existing ListHandler, which already handles filtering and sorting shortlinks. The following excerpt shows how to process the active parameter and embed it in the UrlMappingFilter:

The central point is the line:

Here, the query parameter active is read, interpreted as an optional boolean. The process in detail:

  1. first(query, "active") reads the first value of the query parameter active – something like:
    1. active=true
    1. active=false
    1. or the parameter is missing completely.
  2. parseBoolean(...) converts this value to an Optional<Boolean> . This means that invalid or missing values can also be caught cleanly.
  3. .orElse(null) ensures that if input is missing or cannot be interpreted, the value null is transferred to the filter.

This results in the following meaning:

  • active=true → only active shortlinks
  • active=false → only inactive shortlinks
  • no parameter → no restriction

The UrlMappingLookup then processes the resulting UrlMappingFilter:

These methods replace the application of the filter to the saved shortlinks. By passing through the active state, the lookup layer can precisely select the entries corresponding to the desired activity state.

Finally, the results are transferred to the DTO structure and returned as a paginated JSON response:

With this extension, the ActiveState filter fits seamlessly into the existing filter system. Users can now target active or inactive shortlinks without additional endpoints or separate calls. This shows how ActiveState is processed and how it affects the final JSON result.

HTTP status code semantics: 410 vs. 404

With the introduction of the active/inactive model, the behaviour of the forwarding logic also changes. For the user, the state of a shortlink must be clearly and unambiguously communicated to the outside world – especially if a redirect cannot be done as expected. For this reason, the system uses two distinct HTTP status codes to distinguish expired and disabled shortlinks.

A shortlink can no longer be reached for two reasons:

  1. It has expired. The expiration date has passed, and the shortlink is no longer valid as planned.
  2. It has been disabled. The user has manually disabled the shortlink, regardless of the expiration date.

Although both result in no redirect, they differ in meaning. Expired shortlinks are intended to signal that their lifespan is ending clearly. Disabled shortlinks, on the other hand, have been deliberately taken out of service and may be in a temporary state.

Two HTTP status codes represent this semantic separation:

  • 410 Gone – for expired short links. This status indicates that the resource is permanently unavailable and will not become available again.
  • 404 Not Found – for disabled shortlinks. Although the shortlink exists, its redirect is intentionally not carried out. The 404 status code indicates a temporary or permanent error.

This distinction provides users, API clients, and monitoring systems with more precise feedback on the shortlink’s state. Understandably, a shortlink is not accessible due to a natural process or a manual decision.

Cheers Sven


Discover more from Sven Ruppert

Subscribe to get the latest posts sent to your email.

Leave a Reply