Dynamic routes

Dynamic routes are used to load layouts based on a structure in url format. These simplify the work needed to setup a page and allow for easy communication between dynamic components.

Requirements

  • Fixed structure
  • Clean URL compliance wiki
  • Unlimited length
  • Component parameter support
  • Pass information up/down the component tree
  • Support commands

Fixed structure

Consider this url as an example:

https://my-tool.org/employees/details/id/aaa-aaa-aaa/account/bbb-bbb-bbb/transaction/ccc-ccc-ccc

The url has the following parts:

  • host https://my-tool.org: this denotes the application url. It is the entry point into the application
  • layoutSection employees: used to group together layouts, and provides more context from a URL to the user
  • layoutName details: specifies that the details layout from the group employees should be loaded
  • data id/aaa-aaa-aaa/account/bbb-bbb-bbb/transaction/ccc-ccc-ccc: The rest of the url is data that has no effect on the layout. This data will be passed down to the components.

This gives us the following structure of URLs:

<host>/<layoutSection>/<layoutName>[/<key>/<value>]*

The key/value pairs are not required and the url can supply as many as is needed.

We will omit the host part for the remainder of this document

Implementation

Loading the layout

In the frontend a page will be created that captures the entire url. The page will load a dynamic layout from the backend given the layoutSection and layoutName.

Important

Considder this example: /employees. This url does not contain a layoutName section and loading would fail. A decision can be made to default the layout name to dashboard when none is provided. Only if this layout is not available from the backend will we redirect to the 404 page.

Passing data

The dynamic layout will then process the remainder of the URL. It will create a key/value list of the pairs in the url and pass it down to the layout itself.

Considder our previous example: employees/details/id/aaa-aaa-aaa/account/bbb-bbb-bbb/transaction/ccc-ccc-ccc

This would load the layout details from employees section. It would then create this key/value list and pass it down:

1
2
3
4
5
6
7
{
  "layoutSection": "employees",
  "layoutName": "details",
  "id": "aaa-aaa-aaa",
  "account": "bbb-bbb-bbb",
  "transaction": "ccc-ccc-ccc"
}

Components from the dynamic layout can use this data as they please. The information would be available when needed.

Important

We include the layoutSection and layoutName in the data. This will be needed to load the correct service to handle requests and commands.

Flexibility

Using URLs this way make them very flexible. The following urls are all equal in how the UI reacts:

  • employees/details/id/aaa-aaa-aaa/account/bbb-bbb-bbb/transaction/ccc-ccc-ccc
  • employees/details/account/bbb-bbb-bbb/transaction/ccc-ccc-ccc/id/aaa-aaa-aaa
  • employees/details/account/bbb-bbb-bbb/id/aaa-aaa-aaa/transaction/ccc-ccc-ccc

As long as the key value pairs stay together the part after the layoutName will load the page correctly.

Command pattern

The dynamic URLs solve a lot of problems by allowing a command pattern through. Considder this url:

employees/details/id/aaaa/do/delete

The component that is responsible for loading id aaaa from the employees service receives an extra command parameter do. this signals the component to perform a delete action for the given id.

At this point the record might be deleted, but the question becomes where do we go next? The layout can’t load this because the record with the given id no longer exists. We have a couple of ways to tackle this:

  • Default: decide on a default rule to load a layoutName list
  • Key/value: add data to the url and pass the desired destination
Note

A combination of these 2 will provide the most flexible implementation. Where the system redirects might be different depending on business logic. Therefor it is best to first check for additional data. If that is not present, redirect to the default layout. Redirecting would result in a 404 if the route can not be found and this is the desired behaviour.

To redirect you can add parameters to the url/data:

  1. target-section: the section to redirect to after completion
  2. target-layout: the layout to redirect to for the given section
Note

if the target-section key is not defined, you can redirect to the original section.

Processing actions

To process an action, the first component that understands the action that was requested would execute it. The following steps would occur:

  1. Check if Do parameter exists
    1. Check security with the csrf token
    2. Execute the action
    3. Redirect either to the default layoutName or to the endpoint specified by the data
  2. Load the layout
    1. Parse the url
    2. Throw error or skip pairs on keys starting with _
    3. Load the requested layout

Security

The command pattern allows users to delete records through the url. Therefor we will implement a request forgery implementation. This will make sure that actions can only be triggered from within the UI.

To achieve this action buttons need to do 2 things:

  1. Add a new GUID to the data object
  2. Add the GUID to the action URL

For the component that executes the command the following actions are needed:

  1. Check for the token in the data object.
  2. If it exists, compare it with the token in the url
  3. If it is equal, do the requested action

Upon failure, an error is shown to the user, and the layout does not refresh.

So to implement, the action button adds an _csrfToken to the data:

1
2
3
4
5
6
7
8
{
  "layoutSection": "employees",
  "layoutName": "details",
  "id": "aaa-aaa-aaa",
  "account": "bbb-bbb-bbb",
  "transaction": "ccc-ccc-ccc",
  "_csrfToken": "xxx-xxx-xxx"
}

Then the button can call the url employees/details/id/aaaa/do/delete/csrf/xxx-xxx-xxx. Only when the parameter csrf matches the _csrfToken in the data, will the action be executed.

Important

As a rule, the parameters parsed from the url can never start with a _! This would allow to bypass the security measures. Therefor the mechanism should throw an error when an underscore is found in one of the url parameter keys.