Sometimes the biggest obstacle when building a new app or project is security. It seems that each technology has its own special set of concerns. How can we be sure that we're protecting our resources? I was using NodeJS and setting up an express server the first time I used middleware.
Thankfully, NodeJS wasn't that new. There was plenty of easy libraries to add quick ways to protect your routes.
Similarly, although FastAPI isn't old, there are quick ways that we can prevent unnecessary or unauthorized use of our API. In this article, we'll take a look at some of those methods.
What is Middleware?
Middleware works in between the server and the routes. We can use it to reject requests, add headers, remove data from response objects, process, analyze, and track the flow of requests to all of our routes or some of our routes.
You can do many things with middleware, but a few common use cases include:
- Adding headers
- Adding properties to request
In FastAPI, we add middleware to the app with the
add_middleware function. FastAPI covers some basic use cases that we can add with little configuration.
First, let's add some middleware that checks the host header on incoming requests.
from fastapi.middleware.trustedhost import TrustedHostMiddleware # ... app.add_middleware( TrustedHostMiddleware, allowed_hosts=["localhost", "app.herokuapp.com"], )
We imported the middleware from
fastapi.middleware. This is a library that provides some plug-and-play options.
Then, we passed the imported object as the first argument to the function. After that, depending on the middleware, we have configuration options.
In the above example, this means adding the two domains that are correct hosts in production and development. Granted, localhost should not be used as a valid host in production.
Cross-origin-resource-sharing declares what resources can be shared between browsers or different domains.
We can set up a global CORS configuration with such a small API and not worry about blocking important access.
CORS 'allowed origins' often change with the environment.
from fastapi.middleware.cors import CORSMiddleware #... origins = dict( development=["http://localhost:8000"], production=["https://production.url"] ) app.add_middleware( CORSMiddleware, allow_origins=origins[settings.ENV], allow_credentials=True, allow_methods=["GET"], allow_headers=["*"], )
Therefore, we are changing the allowed origin (where the request is coming from) based on whether we are in production or development (more on this in a little bit).
allow_credentialsspecifies whether or not we are accepting cookies in the request.
allow_methodsdetails the HTTP request methods that are allowed for the resource.
allow_headersis—intuitively—the headers that the resource will accept.
CORS will not be applied for requests sent from API clients (like Postman) or mobile devices.
OK! We have already secured our API in a couple of important ways. However, let's take a quick break to explain how we set environment variables in FastAPI.
First, we need to create a class to set the types for our environment variables.
from pydantic import BaseModel, BaseSettings #... before app is declared class Settings(BaseSettings): SANITY_API_KEY: str API_KEY: str ENV: str = "production"
I declare the
Settings class just like any other typed object. However, instead of passing in the
BaseModel class, I pass in the
I can set a default value (like I did for
ENV) just like I would set a default value in a response or request object. If no environment string is passed, I want the app to execute like it would in "
Next, we build the object with the relevant values by calling the
Settings class and initializing a
# this has to be done after we declare the class Settings settings = Settings() app = FastAPI( # ... )
But how do we pass in the variable values?
A simple way to declare environment variables is at the time we start the server.
$ SANITY_API_KEY=123 API_KEY=123 ENV=development uvicorn main:app --reload
It really is that easy. With a service like Heroku, you can inject these values as secrets through their cloud platform. That allows developers to not hardcode these values, like an API key into code.
This was a very quick middleware and environment variables article. With FastAPI, things are pretty simple, thus making it easy to explain and set up.
In the next article, I'll show how to check an API key header for a secret key.