Problem:

In most web frameworks we can define a request handler with app.get('/items/{id}') and item will get passed in to the function as a parameter. We see it and use it every day. But, have you ever thought how its done? Lets find out!

Example in FastAPI

# test.py
from fastapi import FastAPI

app = FastAPI()
@app.get("/items/{id}")
def get_item(id):
    return {"id": id}

Run it with this command:

# install dependencies if you have't already
pip install fastapi uvicorn

# Run it
uvicorn test:app --reload

and call it:

curl localhost:8000/items/123
# Result
{"id": "123"}

Solution 1: Using Regular Expressions

The idea is to take a string like '/items/{id}' and make it into a regular expression where we can use named groups to name things out and extract the parts.

# parse_with_regex.py
import re

def extract_params(path_definition, incoming_path):
    # Convert it to 'items/(?P<id>.+)'
    pattern = path_definition.replace('{', '(?P<').replace('}', '>[^/]+)')

    matches = re.match(pattern, incoming_path)
    return matches.groupdict()


if __name__ == '__main__':
    print(extract_params(
        path_definition='/items/{id}',
        incoming_path='/items/123'
    ))
# output:
# {'id': '123'}

Code readability suffers here. This works at first. But as we add more test cases it starts falling apart very quick. Like when our path definition wants to be a bit more greedy, {id+} for example in AWS API Gateway. Also, what happens when our test contains the extra '/' at the end. The regular expression will get too complicated very fast. It is already not that readable with all string replacements. Let's see if we can do better!

Solution 2: Using Arrays

Let's explore the fact that HTTP paths are separated by '/'. So, we can split by it and compare segements


def extract_params(path_definition, incoming_path):

    definition_parts = path_definition.split('/')
    incoming_path_parts = incoming_path.split('/')

    result = {}

    for index, part in enumerate(definition_parts):
        if '{' in part:
            result[part[1:-1]] = incoming_path_parts[index]

    return result


if __name__ == '__main__':
    print(extract_params(
        path_definition='/items/{id}',
        incoming_path='/items/123'
    ))

# output:
# {'id': '123'}

With this approach, the code is easily extendable. Just add more conditions as we inspect the segments.