Gmail with FastAPI
Introduction
In a world where technology moves at the speed of light, FastAPI is the newest package that provides developers with a quick setup, fast-network solution for controlling backend APIs. Its power and ease of use make this framework a top choice for web servers today. Coming loaded with a standard package of network controls, FastAPI provides pythonic methods for everything from routes and automatic model serialization to swagger generation.
In this tutorial, we will walk through a simple example of routing traffic through a backend web server to Gmail for managing a user’s messages. We will go over basic features of FastAPI such as routes, dependency injection, and the underlying Pydantic library. By the end, you will be ready to connect your local web server to the Google API or any other public web interface.
What Is FastAPI And Why Use It For Routing?
FastAPI has quickly become the go-to library for creating web servers in Python. It offers a full package of network tools including simple-to-use router annotations, CORS middleware, serialization, and automated swagger docs. Under the hood, FastAPI leverages both Pydantic and Uvicorn to host endpoints and serialize objects at speeds that are considerably faster than Flask and Django, older options for web hosting. Don’t believe it? Check out TechEmpower for benchmarking Python server technologies.
According to the Python Developer Survey, FastAPI became the most-downloaded Python web framework as of August 2025, growing from 29% to 38% adoption over the past year — more than 4M downloads a day, per FastAPI’s own announcement. Given these numbers, FastAPI proves itself to be the optimal choice for serving backend data connections in Python. So, without further ado, let’s start up a server and connect with Gmail.
Setting Up FastAPI
To get started, let’s install the underlying dependencies in our Python environment.
pip install pydantic uvicorn fastapi
These three dependencies are all you need to spin up a local server and start hosting your API with automatic object serialization and even an autogenerated swagger doc as a freebie.
Now, to create the server, open a new file in your Python directory and call it
fastapi-server.py. FastAPI works on the standard model where your main block will
start up a server which takes a FastAPI object loaded with all your custom
configurations.
In the following code, we use the uvicorn dependency we just installed as the core
server, serving on localhost at port 8000.
from fastapi import FastAPI
app = FastAPI()
if __name__ == '__main__':
import uvicorn
uvicorn.run(
"fastapi-server:app",
host="localhost",
port=8000,
reload=True
)
This code doesn’t do much, but it will serve as the foundation for our entire
application. For a local app we run with reload=True in uvicorn; it’s important to
note that production applications will not use this. Now let’s add some routers.
Defining Simple Routers And Pydantic Models
Working with FastAPI, we use the primary app object we’ve created and add all
desired routes and configurations to it, so that the uvicorn server can run it all in
concert. For example, if we want to add a simple “health” endpoint to our server, we
create an APIRouter object defining a health endpoint and add it to our application.
from fastapi import FastAPI, APIRouter
app = FastAPI()
health_router = APIRouter()
@health_router.get("/health")
def health_check():
return {"status": "ok"}
app.include_router(health_router)
if __name__ == '__main__':
import uvicorn
uvicorn.run(
"fastapi-server:app",
host="localhost",
port=8000,
reload=True
)
In the above example, we built a /health endpoint onto the health_router,
specifying the HTTP method as GET. This route simply responds with a dictionary
showing our status is “ok” — a common practice in web servers. Note that Pydantic does
all the work of serialization here: we didn’t have to define any serializers to stream
this response across the network. It’s connected out of the box to return a valid HTTP
response from the endpoint regardless of what we send back.
Let’s expand on this by creating a new Gmail endpoint to read the number of messages a user has in their mailbox.
To simplify HTTP calls to the Gmail API, we’ll use Google’s Python libraries. Install the following dependencies in your Python environment to get started:
pip install google-auth google-auth-oauthlib google-api-python-client
These libraries help us configure OAuth 2.0 credentials and build a Gmail client. This
configuration requires you to go to the Google Developer portal and set up your app to
obtain the client ID and client secret values. Walking through that process is a large
topic on its own, so I’ll cover it in another tutorial. For now, I’ll assume you have
the client credentials in a file named credentials.json.
Now let’s add a new gmail_router object with the path prefix="/gmail". Here we host
the path /inbox, where we serve clients their email address and message count. Then
we add our new endpoint to the app object via gmail_router.
from fastapi import APIRouter, Depends, HTTPException
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
def get_gmail_service():
# Load credentials from file or environment
creds = Credentials.from_authorized_user_file("credentials.json", [
"https://www.googleapis.com/auth/gmail.readonly"
])
service = build("gmail", "v1", credentials=creds)
return service
gmail_router = APIRouter(prefix="/gmail")
@gmail_router.get("/inbox")
def read_inbox(service=Depends(get_gmail_service)):
try:
profile = service.users().getProfile(userId="me").execute()
messages = service.users().messages().list(userId="me").execute()
message_count = messages.get("resultSizeEstimate", 0)
return {
"email": profile["emailAddress"],
"message_count": message_count
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
app.include_router(gmail_router)
We now have a simple API server that can provide a user with Gmail connectivity. From here, we can scale up our application to whatever limits we want. The libraries provided by Google contain a whole host of endpoints for communicating with Gmail, which can be leveraged for all sorts of projects to manage or automate a user’s email.
Dependency Injection For Singletons
Now let’s talk about some of the technical configuration in FastAPI that abstracts away most of the usual REST or Python plumbing.
Notice above that read_inbox() takes a service parameter, which is the return value
of FastAPI’s Depends(). Depends handles dependency injection: it requires its
argument to be a pre-defined function returning an object of matching type, like we did
with get_gmail_service().
Dependency injection matters in asynchronous web hosting for several reasons, including
providing a singleton pattern for creating objects like our Gmail service. Python
doesn’t enforce this pattern — we could still instantiate the service object some other
way — but FastAPI’s Depends gives us a framework that’s easily repeatable.
This pattern also makes it easier to follow the flow of data through your API, since the dependency structure is defined throughout your project. Circular dependencies cause runtime errors, which enforces a single direction for object creation. This leads to better scalability by reducing cognitive complexity and making for cleaner code. Dependency injection and singletons should be the default in production-ready asynchronous APIs.
Handling Origin Security With CORS Middleware
Before we move on, let’s briefly cover CORS middleware and some security concepts.
Browsers typically limit cross-origin requests: if a frontend page is hosted at one
location and requests data from another, the browser discontinues the request. This is
the “same-origin policy,” which lets the browser halt potentially malicious attacks
against your server, an attack class known as cross-site scripting (XSS). Since this is
the default for browsers, we need a mechanism to allow cross-origin resource sharing
(CORS). Browsers that enforce same-origin policy add an Origin header in a preflight
check when requesting resources from a separate domain, and expect the server to
respond with an allow list for the request. If that passes, the browser moves forward.
This preflight and allow list is standard browser-server communication, so let’s add
one more piece of configuration to our FastAPI server:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["localhost:8000"], # Adjust as needed
allow_credentials=True, # Allow cookies and authorization headers
allow_methods=["GET", "POST", "DELETE"], # Allow specific HTTP methods
allow_headers=["*"], # Allow all headers
)
Here we’ve added a middleware to our app object. Our server can now work together
with a browser to list the domains we expect to request our data from. This would help
if, for example, your browser loaded a dashboard hosted at localhost:8000 while your
app itself was hosted in the cloud at somewhere.net.
Note that this does not directly secure your server, nor does it prevent other clients from calling your API — that requires actual authentication. CORS only works in conjunction with a properly configured browser.
Read more on this topic on MDN’s CORS guide.
With the configuration done, let’s check out the automatically generated swagger docs.
Automatic Swagger Documentation
After finishing your API service, it’s always important to document it so others know
how to use it — and FastAPI provides these docs out of the box. Run your server
application and point your browser to http://localhost:8000/docs.
This route takes you to a Swagger page generated automatically from the routes on your
app object, and it updates as you build more endpoints, add parameters, or change the
configuration on app.
Conclusion And Next Steps
After following along with this tutorial, you’ve built a simple API server that can host a user’s Gmail account. We covered core FastAPI concepts like routers and models, dependency injection and singletons, CORS, and Swagger docs. With these basics, you’re ready to automate email processing or connect to other APIs like Meta or Reddit.
In a future post, I’ll cover the Gmail application setup in more depth, since the Google developer dashboard can be daunting. In the meantime, reach out and let me know what you thought — thanks for reading TechNoise!