Commit Hash: 7292bec
If you’ve ever worked at a company that has a sizable codebase before, odds are you’ve most likely dealt with a mono-repo. A mono-repo is a single git repository that contains most if not all of a companies code. Why do so many organizations have a mono-repo? We’ll discuss the motivations and philosophy behind this, and various topics we cover in the future will additionally build on this.
A Lay of the Land
Typically, there’s a few ways that organizations structure their code: multi-repo, mono-repo, or monolith. A multi-repo setup is where each service is split into its own repository, a mono-repo is a single repository that contains all services, and a monolith is a single service that encapsulates all functionality.
I won’t go too much in detail about each of these since there’s a good amount of posts online about this topic, but it’s important to note that none of these are a “golden bullet”. That is, each approach has its pros and cons depending on your codebase structure, company size, and development cycle. Generally, though, monoliths have fallen out of favor for larger teams and codebases since they do not scale well. Imagine our Jarvis service as a monolith: we would have everything running as a single service. If for some reason the chat feature was heavily used, there would be no way to scale just that portion of our product.
Between a multi-repo and mono-repo setup, I personally favor a mono-repo. Most of the largest tech companies in the world favor mono-repos, too: Google, Meta, Airbnb, and Uber. The main benefits over a multi-repo are that code reusability is encouraged, dependency management is easier, and doing things cross-service is easier. For example, if you needed to write a feature that required changes in two different services, you could do that in a single git commit in a mono-repo. Additionally, things like integration tests are easier to write if all code is in the same repository (which we’ll revisit when we look at CI/CD). However, a mono-repo can become complex to navigate/understand, and can take a looong time to initially clone (Google’s monorepo is hundreds of terabytes!!).
Jarvis Mono-Repo
We’ll be adopting a mono-repo structure for Jarvis. If you take a look at the commit associated with this lab, you’ll see that the structure has changed a bit from what it was before. Namely, we now have two services, auth and notes. As the name suggests, the notes service handles the API for getting, creating, and editing notes. Each of these is managed as its own uv
project under backend/services/<service>/
. This means that each service can manage its own dependencies, e.g. the pyproject.toml
files. Additionally, we refactored some shared code into a backend/utilities/
folder. This is also its own uv
project, but is built as a dependency. In the pyproject.toml
file, this is the lines
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
which says to use the hatchling
tool to build our package. Then, you can see how each service imports the utilities as a package in their pyproject.toml
:
[tool.uv.sources]
shared = { path = "../../utilities", editable = true }
This tells uv
that we have a package that should come from ../../utilities
called shared
. We then import shared
in the dependencies
list. Finally, we can import code from the shared
package as we would with any other package, e.g. from shared.database import Base
.
Right now, this change might seem strange, but as we’ll see throughout the course, this codebase structure can make our lives much easier later down the line. Eventually, Jarvis will be a system that runs with 3 services, of which each will have nodes with different roles. A mono-repo structure will help make these distinctions clear.
Jarvis Authentication Flow
Before we get to migrations, let’s go over how Jarvis is doing authentication. When we first installed Jarvis, we tested the signup and login flows. However, that was all on the auth service. How would something like the notes service authenticate requests? There are quite a few paradigms, but we’ll be using Bearer tokens. Any authenticated requests will expect an HTTP header Authorization: Bearer <token>
. This token is the token that is issued from the auth service. Each service will then make a network request to the auth service to determine if the provided token is valid or not.
%%{init: {'theme':'dark'}}%% sequenceDiagram participant Client participant NotesService as Notes Service participant AuthService as Auth Service Client->>NotesService: GET /notes NotesService->>AuthService: GET /validate AuthService-->>NotesService: UserResponse NotesService-->>Client: Notes data (200 OK)
Let’s test this! We’ll need to open two terminals, one to spin up the auth service and one to spin up the notes service. Run the following commands:
# from backend/services/auth
uv run fastapi dev main.py --port 8000
# from backend/services/notes
uv run fastapi dev main.py --port 8001
First, we’ll need to get a token from the auth service. Navigate to localhost:8000/docs
and hit the /token
endpoint to get a token. You’ll just need to fill out the username and password fields, you can leave the rest as is. Then, we’ll need to use something like Postman or curl to hit the notes service. Unfortunately, since the auth and notes service are two separate FastAPI applications, there’s no nice built in way to have the token used automatically. Instead, we’ll need to manually make the request. Using curl, we can hit the /notes
endpoint like so
curl --location --request GET 'localhost:8001/notes' \
--header 'Authorization: Bearer <token>'
You should see a response of simply []
, since we don’t have any notes yet. Let’s create a note:
curl --location --request POST 'localhost:8001/notes' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"title": "My note",
"content": "Some content"
}'
# response
{"id":1,"user_id":1,"title":"My note","content":"Some content","created_at":"2025-09-03T20:37:19.335101","updated_at":"2025-09-03T20:37:19.335109"}
Now, if we get /notes
again, we should see the note we just created. Great!
Jarvis Frontend
Additionally, you’ll notice that there’s a new frontend/
folder in the repo now as well. This is written in next.js as a single-page-application with a backend-for-frontend. Specifically, any requests from the frontend are made directly to the next.js server, which then proxies them to the backend. We discussed a few of the reasons why this paradigm might be good in lecture, but one large benefit with our OAuth2 setup is that the browser does not need to store any sensitive credentials or tokens. Instead, the BFF handles token management for us by using server-side cookies. You don’t need to be worried about this is done technically, but it does mean in the future we will need to deploy our frontend as a server. So, technically, the authentication flow from above looks like:
%%{init: {'theme':'dark'}}%% sequenceDiagram participant Client participant BFF as BFF participant NotesService as Notes Service participant AuthService as Auth Service Client->>BFF: GET /notes BFF->>BFF: Token from cookies BFF->>NotesService: GET /notes NotesService->>AuthService: GET /validate AuthService-->>NotesService: UserResponse NotesService-->>BFF: Notes data BFF-->>Client: Notes data (200 OK)
You can specifically see the routes we have defined at frontend/app/api/
. The paths under auth/
are the handlers for the OAuth2 flow, and the router under proxy/[...slug]/
handles any request of the form /proxy/...
.
Now, let’s get the frontend up and running. First, install the latest version of Node.js if you don’t have it already. Then, navigate to the frontend/
folder and run
npm install
This will install the various packages needed for the frontend, similar to how uv
does it for our backend services. Once this is done, you can run the frontend server with
npm run dev
Navigate to localhost:3000
in your browser and you should see a login page. Login with the credentials you created (or sign a new user up). You should then be able to play around with the notes features. Try creating, editing, and deleting some notes. The chat feature will not work as we don’t have a chat service yet.