A simple, easy-to-use blog application built with FastAPI.
- 📝 Write blog posts in Markdown
- 🎨 Syntax highlighting for code blocks
- 📱 Responsive design
- 🌙 Dark mode
- 🎯 Overloadable templates
- 📚 Live, working configuration examples
- 🔍 SEO-friendly
- 🗺️ Sitemap generation
- 🐳 Docker support
- ⚡ Fast performance with FastAPI
- 🔒 Modern security practices
- 🧪 Comprehensive test coverage
- 🚀 Python 3.12+ and 3.13 support
- Import the
add_blog_to_fastapifunction - Run the instantiated FastAPI app through the
add_blog_to_fastapifunction
This all you need to do:
from fastapi_blog import add_blog_to_fastapi
from fastapi import FastAPI
app = FastAPI()
app = add_blog_to_fastapi(app)
@app.get("/")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": "http://localhost:8000/blog",
}- Add the first blog entry
Assuming your FastAPI app is defined in a main.py module at the root of your project, create a file at posts/first-blog-post.md:
---
date: "2024-03-21T22:20:50.52Z"
published: true
tags:
- fastapi
- fastapi-blog
title: First blog post
description: This is the first blog post entry.
---
Exciting times in the world of fastapi-blog are ahead!
## This is a markdown header
And this is a markdown paragraph with a [link](https://github.com/awestley/fastapi-blog).- Add the first page
Assuming your FastAPI app is defined in a main.py module at the root of your project, create a file at pages/about.md:
---
title: "About Me"
description: "A little bit of background about me"
author: "Daniel Roy Greenfeld"
---
## Intro about me
I'm probably best known as "[pydanny](https://www.google.com/search?q=pydanny)", one of the authors of Two Scoops of Django.
I love to hang out with my [wife](https://audrey.feldroy.com/), play with my [daughter](/tags/uma), do [Brazilian Jiu-Jitsu](https://academyofbrazilianjiujitsu.com/), write [books](/books), and read books.
- [Mastodon](https://fosstodon.org/@danielfeldroy)
- [LinkedIn](https://www.linkedin.com/in/danielfeldroy/)
- [Twitter](https://twitter.com/pydanny)
## About this site
This site is written in:
- Python
- FastAPI
- fastapi-blog
- Sakura minimal CSS framework
- Markdown
- Vanilla HTMLfastapi_blog is configurable through the add_blog_to_fastapi function.
Change the main app to mount StaticFiles:
from fastapi_blog import add_blog_to_fastapi
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app = add_blog_to_fastapi(app)
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": "http://localhost:8000/blog",
}This example is Django-like in that your local templates will overload the default ones.
import fastapi_blog
import jinja2
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
django_style_jinja2_loader = jinja2.ChoiceLoader(
[
jinja2.FileSystemLoader("templates"),
jinja2.PackageLoader("fastapi_blog", "templates"),
]
)
app = FastAPI()
app = fastapi_blog.add_blog_to_fastapi(
app, prefix=prefix, jinja2_loader=django_style_jinja2_loader
)
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": f"http://localhost:8000/blog",
}Perhaps you want to have the blog at the root?
import fastapi_blog
from fastapi import FastAPI
app = FastAPI()
app = fastapi_blog.add_blog_to_fastapi(
app, prefix="change"
)
@app.get("/api")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": "http://localhost:8000/change",
}This is for when your blog/CMS needs to be at the root of the project
import fastapi_blog
from fastapi import FastAPI
app = FastAPI()
@app.get("/api")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": "http://localhost:8000",
}
# Because the prefix is None, the call to add_blog_to_fastapi
# needs to happen after the other view functions are defined.
app = fastapi_blog.add_blog_to_fastapi(app, prefix=None)import fastapi_blog
from fastapi import FastAPI
favorite_post_ids = {
"code-code-code",
"thirty-minute-rule",
"2023-11-three-years-at-kraken-tech",
}
app = FastAPI()
app = fastapi_blog.add_blog_to_fastapi(app, favorite_post_ids=favorite_post_ids)
@app.get("/")
async def index() -> dict:
return {
"message": "Check out the blog at the URL",
"url": "http://localhost:8000/blog",
}In the pages directory of your blog, add markdown files with frontmatter. You can then find it by going to the URL with that name. For example, adding this pages/about.md to the default config would make this appear at http://localhost:8000/blog/about.
---
title: "About Daniel Roy Greenfeld"
description: "A little bit of background about Daniel Roy Greenfeld"
author: "Daniel Roy Greenfeld"
---
I'm probably best known as "[pydanny](https://www.google.com/search?q=pydanny)", one of the authors of [Two Scoops of Django](/books/tech).Requirements: Python 3.12+ (tested with Python 3.13.7)
# Clone the repository
git clone https://github.com/awestley/fastapi-blog.git
cd fastapi-blog
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install the project in development mode (this handles uv installation automatically)
make install
# Run the example blog
make runpip install fastapi-blog
# Then create your own FastAPI app using the examples in the documentationOr into a Docker container using the local Dockerfile:
docker build -t fastapi-blog .
docker run -d -p 8000:8000 fastapi-blogOr using a prebuilt Docker image from GitHub Container Registry:
docker run -d -p 8000:8000 ghcr.io/awestley/fastapi-blog:latestThis is if you just want to run the application without building it yourself.
# Run all tests with coverage
make test
# Run linting and formatting checks
make lint
# Auto-fix linting issues
make format
# Run type checking
make mypy
# Run all quality checks
make allfastapi-blog/
├── src/fastapi_blog/ # Main package
│ ├── main.py # Core FastAPI integration
│ ├── router.py # Blog routes and views
│ ├── helpers.py # Utility functions
│ └── templates/ # Jinja2 templates
├── tests/ # Test suite
│ └── examples/ # Example configurations
├── .github/workflows/ # CI/CD workflows
└── pyproject.toml # Project configuration
-
Update the version in
pyproject.tomlandfastapi_blog/__init__.py -
Update changelog.md
-
Build the distribution locally:
rm -rf dist
pip install -U build
python -m build- Upload the distribution to PyPI:
pip install -U twine
python -m twine upload dist/*- Create a new release on GitHub and tag the release:
git commit -am "Release for vXYZ"
make tag|
Daniel Roy Greenfeld |
Audrey Roy Greenfeld |