Developer Diary 3: FastAPI, Nginx and Vue
Introduction
This blog post documents two issues I encountered in recent development:
- Route matching conflicts in FastAPI
- Nginx DNS resolution in Docker environments
Additionally, it includes some reflections after using Vue.
FastAPI Route Matching Conflicts
Problem Investigation
This project uses RBAC for permission control on each endpoint via fastapi.Depends. One endpoint required the roles [A, B]. When I accessed it with a user having role A, it returned a 403 error. I started debugging, only to find this endpoint actually only required [B]. No matter how I debugged, even removing the Depends method from the endpoint, the debugger still jumped to the role-checking method (requiring [B]), and the endpoint consistently returned 403.
At this point, I was getting flustered and was no longer troubleshooting effectively. I began suspecting an issue with uvicorn's reload feature, but manually deleting __pycache__ didn't solve the problem.
Finally, after persistently questioning an LLM, I discovered it was a route matching conflict in FastAPI. A route with a Path Variable was actually overriding a more specific route, and FastAPI neither checks for this on startup nor reports an error at runtime, which is something unimaginable when using Spring Boot.
Reproducing the Issue
I defined identical two endpoints in FastAPI and Spring Boot.
1 | from fastapi import FastAPI |
1 |
|
When accessing /test in FastAPI, the read_item method is actually called. This is because in FastAPI, route matching proceeds in the order of definition. Once a route matches, FastAPI does not continue to check subsequent routes for a match. The reader can verify this rule simply by swapping the order of the read_item and hello methods. Therefore, when using FastAPI, remember: always define specific routes before generic ones. However, the problem lies in this "remembering", which comes at a cost. When there are more and more things to remember, some are bound to be forgotten. And it's mentally taxing.
The reader can verify that Spring Boot does not have this problem. I thought FastAPI's syntax resembling Spring Boot's meant it was sufficiently user-friendly, but that's not the case. This is understandable, given that the latest version of FastAPI (0.124.0) is still not a stable release.
Nginx DNS Resolution in Docker Environments
Problem Investigation
In the production environment, I use Nginx as a reverse proxy for backend services. Both are deployed in containers and started with Docker Compose.
However, during testing, I found the frontend received a response with a 502 Bad Gateway error. Checking the Nginx logs, I found an entry like this, clearly indicating a DNS issue:
1 | [error] 22#22: *13 backend could not be resolved (3: Host not found) |
backendis the name of the backend container
My Nginx configuration was as follows:
1 | location /api { |
Cause and Solution
Nginx resolves domain names at startup by default and caches the IP permanently. If the backend is not running at that moment, resolution fails. Even if the backend starts later, Nginx will not re-resolve.
One solution is to use the depends_on syntax to ensure the backend starts before Nginx. However, this approach is limited, reflecting a static perspective that doesn't consider the entire container lifecycle. Container IPs are dynamically assigned and can change upon restart, scaling, etc. Therefore, we need to make Nginx's DNS cache expire periodically and re-resolve to obtain the correct IP. The configuration is as follows:
1 | location /api { |
127.0.0.11is the address of Docker's internal DNS server. It can resolve other container names within the same Docker network;valid=10ssets the DNS cache to expire every 10 seconds, balancing performance and availability;
Reflections on Vue
The existing frontend project uses Vue. Before taking over it, I only knew React, so I spent a few days reading through the documentation of Vue, Vuex, and Vue Router.
I didn't delve deeply into Vue's syntax, just skimmed through it enough to understand. This proved to be the right approach because AI can write correct syntax. Compared to React, Vue's syntax is closer to the MVVM architecture. Given my prior experience developing WPF, I was initially resistant to this, but later realized I didn't need to focus much on the syntax, so it didn't matter.
Learning Vue with a React background makes one view the Composition API as the orthodox approach. The Options API, with its multi-level nested objects, looks dizzying at first glance. In reality, the Options API came first, followed by the Composition API. A large number of existing Vue projects use the Options API, which also influences AI to prefer this style. This is clearly a historical artifact, not something can be criticized excessively. In fact, React is similar: before the advent of Function Components, React projects used Class Components, which added a lot of unnecessary code. When learning React, I used Function Components from the start. Although I've studied Class Components, not having to work on such projects was fortunate. The Vue project I took over uses the Composition API, which is also fortunate.
I recently noticed that Flutter's syntax resembles Vue's Options API. Perhaps after learning Flutter, I'll come to accept the Options API.
While reading Vue's documentation, I often compared concepts with their React counterparts, which helped me understand better and faster. It's not hard to see that Vue and React each have their merits in practical use; there's no absolute winner. In essence, the Vue and React ecosystems aim to solve similar problems: rational abstraction and reuse of frontend code to achieve engineering and systematization in frontend development. This is also the main reason I could get up to speed with Vue quickly.
Although I primarily focus on backend development, I've managed to avoid mistakes common among many frontend developers:
- Focusing on framework's syntax. This only scratches the surface of frontend development and arguably doesn't even count as having started. In my view, the entry standard should be understanding the fundamental problems frontend development aims to solve.
- Seeing only the page, not the data. A typical manifestation is putting all the code for a page in a single file. Leaving aside the lack of object-oriented understanding, the data flow in the code is extremely chaotic; as features multiply, the code becomes unreadable. Modern applications are data-driven; pages are merely representations of data. Those who make this mistake clearly don't understand this principle.
Backend developers can make similar mistakes, albeit manifested differently.
References
- LLM