DEV Community

Cover image for Build a Docusaurus-like Site with FastAPI: Step 2 - Render Markdown
Leapcell
Leapcell

Posted on

Build a Docusaurus-like Site with FastAPI: Step 2 - Render Markdown

Cover

In the previous article, we set up a FastAPI service and used Jinja2 to render a dynamic HTML template.

But if you've used other documentation site tools, you'll know they all work by writing Markdown, which they then use to generate HTML content, rather than writing HTML by hand.

In this article, we will implement this exact feature: reading a local .md file, parsing it into HTML, and injecting it into our web template.

Step 1: Install a Markdown Parsing Library

We need a tool to convert Markdown text into an HTML string. A popular library for this is python-markdown.

Install it by running the following command:

pip install markdown
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Our First Markdown Document

By convention, we will store all our source documents in a new docs/ folder.

First, create a docs folder in your project's root directory, and then create a hello.md file inside it.

Updated project structure:

fastapi-docs-site/
├── docs/               <-- New
│   └── hello.md        <-- New
├── main.py
├── static/
└── templates/
Enter fullscreen mode Exit fullscreen mode

Edit docs/hello.md:
Open this new file and write some Markdown content so we can test it later.

# Hello, Markdown!

This is our first Markdown document rendered with FastAPI.

Here is a code block example (though it doesn't have syntax highlighting yet):

``python
def say_hello():
    print("Hello from FastAPI!")
``

  - This is a list item
  - This is another list item
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a Template for Rendering Documents

The templates/index.html we created in the last article is our homepage. Now, we need a dedicated template to display the content of this Markdown file.

Create a new file in the templates/ folder named doc.html.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{ page_title }} - My Docs Site</title>
  </head>
  <body>
    <h1>{{ page_title }}</h1>

    <hr />

    <div class="doc-content">{{ content | safe }}</div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

{{ content | safe }} tells Jinja2: "The content in this content variable is safe HTML. Please render it directly and do not escape it."

Step 4: Create the Markdown Rendering Route

Next, let's modify main.py to add a new route for our Markdown file, for example, /docs/hello.

It will perform the following actions:

  1. Read the contents of the docs/hello.md file.
  2. Import the markdown library.
  3. Use the markdown.markdown() function to convert the file content into an HTML string.
  4. Pass this HTML string as the content variable to the doc.html template.

Open main.py and modify it as follows:

# main.py
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
import markdown  # 1. Import the markdown library

app = FastAPI()

templates = Jinja2Templates(directory="templates")


# --- This is the homepage route from the previous article ---
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
    """
    Root route, returns the rendered HTML template
    """
    context = {
        "request": request,
        "page_title": "Hello, Jinja2!"
    }
    return templates.TemplateResponse("index.html", context)


# --- 2. This is our new documentation route ---
@app.get("/docs/hello", response_class=HTMLResponse)
async def get_hello_doc(request: Request):
    """
    Read, parse, and render the hello.md document
    """

    # 3. Hard-code the path to the Markdown file
    # (We will make this dynamic in a future article)
    md_file_path = "docs/hello.md"

    try:
        with open(md_file_path, "r", encoding="utf-8") as f:
            md_content = f.read()
    except FileNotFoundError:
        # Simple error handling
        return HTMLResponse(content="<h1>404 - Document not found</h1>", status_code=404)

    # 4. Use the markdown library to convert it to HTML
    html_content = markdown.markdown(md_content)

    # 5. Prepare the template context
    #    Note that we are now rendering doc.html
    context = {
        "request": request,
        "page_title": "Hello, Markdown!", # Temporarily hard-coded title
        "content": html_content         # Pass in the converted HTML
    }

    return templates.TemplateResponse("doc.html", context)

Enter fullscreen mode Exit fullscreen mode

Step 5: Run and Test

Start your server by running uvicorn main:app --reload.

In your browser, visit this URL: http://127.0.0.1:8000/docs/hello

You should see the content of docs/hello.md successfully rendered as an HTML page!

ImageP1

Deploying the Project Online

A docs site is meant to be visited by everyone, so just running it locally isn't enough. Next, we can deploy it online.

A simple deployment option is to use Leapcell. It's a web app hosting platform that can host projects in various languages and frameworks, including FastAPI, of course.

Leapcell

Follow the steps below:

1.Register an account on the website.

2.Commit your project to GitHub. You can refer to GitHub's official documentation for the steps. Leapcell will pull the code from your GitHub repository later.

3.Click "Create Service" on the Leapcell page.

LeapcellImageP1

4.After choosing your FastAPI repo, you'll see Leapcell has auto-populated the necessary configurations.

LeapcellImageP2

5.Click "Submit" at the bottom to deploy. The deployment will complete quickly and return you to the deployment homepage. Here we can see that Leapcell has provided a domain. This is the online address of your blog.

LeapcellImageP3

Summary and Next Steps

With this article, we have implemented the most core feature of a documentation site: dynamic Markdown-to-HTML conversion.

You probably noticed the Python code block in hello.md. Although it was correctly rendered as code, it looks plain, has no color highlighting, and is hard to read.

This is clearly not the experience a documentation site should provide.

In the next article, we will solve this problem: adding syntax highlighting to our rendered Markdown code blocks.


Follow us on X: @LeapcellHQ


Read other articles in this series

Related Posts:

Top comments (0)