FastAPI has been adapted for building modern Python applications. Today, you’re going to pair this lightweight performant framework with Jinja2 template engine and a modern CSS framework, Tailwind CSS. You will then use SQLAlchemy ORM for the database.
Tailwind CSS is a utility-first CSS framework which means it has a list of utility CSS classes that you can use to style your HTML elements. You can style the elements inside your HTML file by matching and mixing.
This tutorial will build upon the Todo app backend that we’ve built before. We will refine the backend to adapt to our changes to the templates that we use for the frontend. An excellent choice for the templates in FastAPI would be a Jinja2 template engine. So we will use this engine to make FastAPI render our HTML files. The HTML files will have the CSS styles coming from Tailwind CSS.
At the end of this tutorial, you will have a boilerplate that you can use for your FastAPI app set up for Tailwind CSS for the frontend and SQLAlchemy ORM boilerplate for the database.
Prerequisite
To follow along with this tutorial, a basic knowledge of Python will suffice and a revisit to our FastAPI backend todo app is preferred.
Create your FastAPI app
To create a FastAPI app, you can create a main.py
file inside your project directory:
mkdir fastapi-tailwindcss-sqlalchemy && cd $_
touch main.py
Install FastAPI and Uvicorn, the ASGI web server:
pip install fastapi uvicorn
Open your main.py
file and add the following minimal working script:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def home():
return {"message": "hello"}
Now, run your app:
uvicorn main:app --reload
Open your localhost with the 8000
port and you’ll see the JSON response that you’ve returned in the home()
function.
Create your Jinja2 template
We want to render an HTML template for our app instead of this dummy JSON. Let’s create a new directory, inside our project directory, and cd
into it:
mkdir templates && cd $_
Create a new file base.html
and add the following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Todo</title>
</head>
<body>
<h1>Todo app</h1>
</body>
</html>
To be able to use Jinja2 template with FastAPI, install the jinja2
library:
pip install jinja2
Now, return to the main.py
script and see how we can use this template so that once the home()
function is invoked, the base.html
template will be rendered.
The main.py
script now looks like the following:
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/")
async def home(request: Request):
context = {"request": request}
return templates.TemplateResponse("base.html", context)
We’ve defined the templates
object and used it to return the base.html
template. The TemplateResponse
class has two arguments, the template and the context
. The context
is a dictionary that has what should be passed to the Jinja2 template. At least, we would need request
which is a Request
object coming from the fastapi
library.
The base.html
template should look like the following:
Let’s move to setting up Tailwind CSS for this project to add more life to it.
Set up Tailwind CSS with FastAPI
You have some options to use Tailwind CSS. You can either use the CDN, which is not recommended for production. That’s because every time you run your app, Tailwind will load every CSS utility and that will make your app slow.
You can also use npm
to install the framework, but we’re not going through this route as this post is directed to Python developers and I assume you may not have knowledge of npm
or you might not have Node.js installed.
So as you might expect, we will use pip
to install TailwindCSS framework. We will install pytailwindcss which requires no installation of Node.js and provides a Tailwind CSS CLI that we can use to interact with the framework.
So let’s install the framework:
pip install pytailwindcss
You can now use the tailwindcss
command which invokes the Tailwind CSS CLI. You can run it to download the binary:
tailwindcss
After the download is successful, you should see the help output of the tailwindcss
command.
Now, make sure you’re inside the fastapi-tailwindcss-sqlalchemy
directory which is the project folder. You can create a new Tailwind CSS project with the init
subcommand:
tailwindcss init
A Tailwind CSS config file should be created now inside the project directory called tailwind.config.js
. Open that file and modify it to the following:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["templates/*.html"],
theme: {
extend: {},
},
plugins: [],
}
The content
object now refers to all HTML files inside the templates
directory in our FastAPI project. This is essential to make Tailwind CSS know where our templates are.
You can leave the terminal that has the backend server running and open a new one. This new terminal will have our frontend updated once we have our Tailwind set up.
Create a new folder styles
and inside that folder create a new file main.css
which should contain the following:
@tailwind base;
@tailwind components;
@tailwind utilities;
These are CSS directives that Tailwind CSS uses to understand the CSS utilities that we will use in our templates.
Now, create a new folder static
that contains another new folder css
. Inside the latter, create a new file main.css
. We will configure this file so that FastAPI can read it through its static files and be able to apply it to our templates.
You need now to convert the Tailwind CSS directives inside the styles/main.css
file to an actual CSS file converted by Tailwind CSS that resides in static/css
path:
tailwindcss -i styles/main.css -o static/css/main.css --watch
The --watch
option here will watch any change that could happen to the styles/main.css
and convert it to the output file in the static directory.
If you run this command, you should see something like the following:
Rebuilding...
Done in 365ms.
This will rebuild every time the input CSS is changed. In our case, we can close the terminal as we will not add any more Tailwind CSS directives to the styles/main.css
file.
To mount the static files in our FastAPI project, add the following two lines to the main.py
script:
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
And to refer to that static file in the template, open the base.html
template and add the following line inside the head
HTML tag:
<link href="{{ url_for('static', path='/css/main.css') }}" rel="stylesheet">
Let’s open Tailwind CSS documentation and head over to the heading section to test out our configuration. You’ll only need to copy and paste the code in there and customize it to your preference. Open the base.html
file and change the h1
HTML tag to have the following classes:
<h1 class="font-medium leading-tight text-5xl mt-0 mb-2 text-blue-600">Todo app</h1>
The template now looks more blue as the following:
Let’s add more to our templates and use a navigation bar from Tailwind.
Create a Tailwind navbar
Open your templates
directory and create a new template, navbar.html
. Copy the Tailwind CSS code block for your desired navbar and paste it to your file:
<nav class="relative w-full flex flex-wrap items-center justify-between py-3 bg-gray-100 text-gray-500 hover:text-gray-700 focus:text-gray-700 shadow-lg">
<div class="container-fluid w-full flex flex-wrap items-center justify-between px-6">
<div class="container-fluid">
<a class="text-xl text-black font-semibold" href="/">Todo</a>
</div>
</div>
</nav>
This simplistic navbar would have just a Todo link to the home page. To be able to use this template, you can include it inside the base template. So add the following line inside the body
HTML tag before the h1
tag:
{% include "navbar.html" %}
This Jinja2 statement should include that new navbar to the base template rendered by the home()
function:
Also add the following after the h1
HTML tag:
{% block content %}
{% endblock content %}
This will help us make use of template inheritance so that every template we create will extend from this base template. We will notice that every content
block inside a new template will be replaced in that content
block inside the base template.
Now, we’re ready to add CRUD operations to our app except that we will add the models to be able to deal with our database.
Create your database models with SQLAlchemy
As we discussed earlier in our backend tutorial, you need to create a models.py
script. You can add the following to it:
from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.engine import URL
from sqlalchemy.orm import declarative_base, sessionmaker
from decouple import config
url = URL.create(
drivername="postgresql",
username=config("DB_USERNAME"),
password=config("DB_PASSWORD"),
host="localhost",
database="mydb",
port=5432
)
engine = create_engine(url)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class Todo(Base):
__tablename__ = "todos"
id = Column(Integer, primary_key=True)
text = Column(String)
is_done = Column(Boolean, default=False)
Base.metadata.create_all(engine)
Notice that we’ve used python-decouple
here to avoid exposing our database username and password instead of hardcoding them. First, you need to install it:
pip install python-decouple
And then you can import the config
file and pass the environment variables to it. In our case, we’ve used DB_USERNAME
and DB_PASSWORD
. These env vars exist on a .env
file. Create that file and pass your credentials to each variable:
DB_USERNAME=<your-username>
DB_PASSWORD=<your-password>
Note: If you want to push these changes to a version source control like Git, you need to exclude the .env
file from being exposed to the public. You’ll need to include it in the .gitignore
file.
Conclusion
We’ve seen a boilerplate to set up Tailwind CSS python package to our FastAPI. We learned about a basic configuration of Jinja2 template to the app. We’ve also known how to set up a basic Todo model through SQLAlchemy ORM.
Next: You’re now ready to build your CRUD functionalities to this FastAPI app using SQLAlchemy ORM for the database, and Jinja2 and Tailwind CSS for the frontend.