> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Path Order Matters

> This article discusses the importance of route ordering in FastAPI to prevent unexpected behavior and type validation errors.

In this lesson, we explore a common pitfall related to route ordering in FastAPI. FastAPI matches incoming requests based on the defined routes, and the sequence in which the routes are declared is crucial for ensuring that each request is handled by its intended endpoint. Misordering routes can lead to unexpected behavior.

## Retrieving a Post by ID

Below is a simple GET endpoint that retrieves a post by its ID. When a request is made to this route, it prints and returns the corresponding post details.

```python theme={null}
@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {"post_detail": post}
```

### Sample Console Output

```plaintext theme={null}
INFO:     Started server process [3048]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
None
INFO:     127.0.0.1:53457 - "GET /posts/asdf sdf HTTP/1.1" 200 OK
None
INFO:     127.0.0.1:53458 - "GET /posts/123123 HTTP/1.1" 200 OK
None
INFO:     127.0.0.1:53462 - "GET /posts/2 HTTP/1.1" 200 OK
```

## Introducing the Latest Post Endpoint

Now, consider adding another GET endpoint intended to retrieve the latest post. The goal is to process a request to `/posts/latest` and return the most recent post.

### An Initial Attempt

The following handler is an initial attempt to implement this functionality:

```python theme={null}
@app.get("/po")
def get_latest_post():
    my_posts = []
    my_posts.append(post_dict)
    return {"my_posts": my_posts}
```

### Console Output for the Initial Attempt

```plaintext theme={null}
INFO:     Started server process [3048]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:53457 - "GET /posts/asdfasdf HTTP/1.1" 200 OK
INFO:     127.0.0.1:53458 - "GET /posts/123123 HTTP/1.1" 200 OK
INFO:     127.0.0.1:53462 - "GET /posts/2 HTTP/1.1" 200 OK
```

## Evolving the Code

The code evolves to determine the latest post by subtracting one from the length of the posts list. In this version, the routes are defined as follows:

```python theme={null}
post_dict['id'] = randrange(0, 100000)
my_posts.append(post_dict)
return {"data": post_dict}

@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {"post_detail": post}

@app.get("/posts/latest")
def get_latest_post():
    my_posts[len(my_posts) - 1]
```

### Console Output Remains Similar

```plaintext theme={null}
INFO:     Started server process [3048]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
127.0.0.1:53457 - "GET /posts/asdfsdf HTTP/1.1" 200 OK
127.0.0.1:53458 - "GET /posts/123123 HTTP/1.1" 200 OK
127.0.0.1:53462 - "GET /posts/2 HTTP/1.1" 200 OK
```

## Storing and Returning the Latest Post

After further modifications, the code tries to store the latest post in a variable and return it:

```python theme={null}
my_posts.append(post_dict)
return {'data': post_dict}

@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {'post_detail': post}

@app.get("/posts/latest")
def get_latest_post():
    my_posts[len(my_posts)-1]
    return {'detail': {}}
```

The logging output still remains the same:

```plaintext theme={null}
INFO:     Started server process [3048]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
None
127.0.0.1:53457 - "GET /posts/asdfasdf HTTP/1.1" 200 OK
127.0.0.1:53458 - "GET /posts/123123 HTTP/1.1" 200 OK
127.0.0.1:53462 - "GET /posts/2 HTTP/1.1" 200 OK
```

## The Corrected Latest Post Route

A new test call is made by copying the URL from a previous request. The updated code now looks like this:

```python theme={null}
@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {"post_detail": post}

@app.get("/posts/latest")
def get_latest_post():
    post = my_posts[len(my_posts)-1]
    return {"detail": post}
```

### Console Output for the Updated Code

```plaintext theme={null}
INFO:     127.0.0.1:53458 - "GET /posts/123123 HTTP/1.1" 200 OK
INFO:     127.0.0.1:53462 - "GET /posts/2 HTTP/1.1" 200 OK
WARNING: WatchGodReload detected file change in ['C:\\Users\\sanje\\Documents\\Courses\\fastapi\\main.py', 'C:\\Users\\sanje\\Documents\\Courses\\fastapi\\main.py.4d1dc43f362bd42d8b85c468f089fc.tmp'] - Reloading...
WARNING: WatchGodReload detected file change in ['C:\\Users\\sanje\\Documents\\Courses\\fastapi\\main.py', 'C:\\Users\\sanje\\Documents\\Courses\\fastapi\\main.py.4d1dc43f362bd42d8b85c468f089fc.tmp', 'C:\\Users\\sanje\\Documents\\Courses\\fastapi\\main.py'] - Reloading...
```

## The Route Conflict Issue

If you change the request URL to `/posts/latest`, you might see the following error response:

```json theme={null}
{
  "detail": [
    {
      "loc": [
        "path",
        "id"
      ],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ]
}
```

<Callout icon="lightbulb" color="#1CB2FE">
  This error occurs because FastAPI processes routes in the order they are defined. The route `/posts/{id}` appears before `/posts/latest`, causing the string "latest" to be interpreted as the integer parameter `id`. Since "latest" is not an integer, FastAPI throws a type validation error.
</Callout>

### Illustrative Route Definitions

Consider the following set of route definitions that illustrate the issue:

```python theme={null}
@app.get("/posts")
def get_posts():
    return {"data": my_posts}

@app.post("/posts")
def create_posts(post: Post):
    post_dict = post.dict()
    post_dict['id'] = randrange(0, 100000)
    my_posts.append(post_dict)
    return {"data": post_dict}

@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {"post_detail": post}

@app.get("/posts/latest")
def latest_post():
    # Intended to return the latest post
    post = my_posts[len(my_posts)-1]
    return {"detail": post}
```

In this configuration, the route `/posts/{id}` catches any requests to `/posts/...`, including `/posts/latest`, because "latest" fits the dynamic segment `{id}`. FastAPI attempts to convert "latest" to an integer, resulting in the error.

## Correcting the Route Order

To resolve this issue, ensure that the specific route (`/posts/latest`) is declared before the dynamic route (`/posts/{id}`). Here is the corrected ordering:

```python theme={null}
@app.post("/posts")
def create_posts(post: Post):
    post_dict = post.dict()
    post_dict['id'] = randrange(0, 100000)
    my_posts.append(post_dict)
    return {"data": post_dict}

@app.get("/posts/latest")
def get_latest_post():
    post = my_posts[len(my_posts)-1]
    return {"detail": post}

@app.get("/posts/{id}")
def get_post(id: int):
    post = find_post(id)
    print(post)
    return {"post_detail": post}
```

With this ordering, a GET request to `/posts/latest` is handled correctly by its dedicated endpoint, while requests for posts by integer ID are processed separately.

## Example Responses

When accessing a specific post by its ID, a successful response might look like this:

```json theme={null}
{
    "post_detail": {
        "title": "title of post 1",
        "content": "content of post 1",
        "id": 1
    }
}
```

For the latest post endpoint, a successful response might appear as follows:

```json theme={null}
{
  "detail": {
    "title": "favorite foods",
    "content": "I like pizza",
    "id": 2
  }
}
```

<Callout icon="lightbulb" color="#1CB2FE">
  Remember that FastAPI evaluates routes in the order they are added. Always define fixed routes (e.g., `/posts/latest`) before dynamic ones (e.g., `/posts/{id}`) to prevent unintended matches and to avoid type validation errors.
</Callout>

For more information on FastAPI routing, check out the [FastAPI Documentation](https://fastapi.tiangolo.com/).

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/python-api-development-with-fastapi/module/b1fa1425-07bf-49c6-b19d-ccded17d940d/lesson/f5656cd1-8318-4ad4-ae7c-ee728c91d141" />
</CardGroup>
