FastAPIでDocusaurus風サイトを構築:ステップ3 - コードハイライト
Olivia Novak
Dev Intern · Leapcell

前の記事では、.mdファイルからHTMLを動的にレンダリングするサポートを追加しました。
しかし、ご覧のとおり、docs/hello.mdのPythonコードブロックはレンダリングされましたが、プレーンで色付けされておらず、読みにくかったです。
この記事では、この問題を解決します。Pygmentsとpython-markdown拡張機能を使用して、Markdownコードブロックに構文ハイライトを追加します。
ステップ1:ハイライト依存関係のインストール
python-markdownライブラリは、「拡張機能」を通じて追加の機能をサポートしています。コードハイライトを実装するには、codehilite拡張機能が必要ですが、これは別のライブラリであるPygmentsに依存しています。
Pygmentsをインストールするには、次のコマンドを実行します。
pip install Pygments
ステップ2:静的ファイルディレクトリとCSSの設定
構文ハイライトの仕組みは、次の2段階のプロセスです。
python-markdownのcodehilite拡張機能は、コードブロック(例:```python ... ```)を特定のCSSクラス(キーワードの.k、文字列の.sなど)を持つHTMLタグに変換します。- CSSファイルを使用して、ブラウザにこれらのCSSクラスが表示すべき色を指示します。
Pygmentsライブラリは、このCSSを生成するための便利なコマンドラインツールを提供しています。
まず、プロジェクトのルートディレクトリにstatic/cssフォルダを作成して、このCSSファイルを保存しましょう。
fastapi-docs-site/
├── docs/
│ └── hello.md
├── main.py
├── static/
│ └── css/ <-- 新規
└── templates/
ステップ3:ハイライトCSSファイルの生成
ターミナルを開き、次のコマンドを実行します。
pygmentize -S monokai -f html -a .codehilite > static/css/highlight.css
pygmentizeパラメータの説明:
-S monokai: ハイライトテーマとしてmonokaiを指定します(default、github-dark、solarized-lightなども試せます)。-f html:-fは、HTML用のCSSを生成したいことを示します。-a .codehilite:-aは、すべてのCSSルールが.codehiliteCSSクラス(python-markdownによって使用されるデフォルトクラス)の下にネストされることを意味します。
完了後、static/css/highlight.cssを開くと、monokaiテーマのCSSルールが内部に生成されているのがわかります。
ステップ4:FastAPIでの静的ディレクトリのマウント
これでCSSファイルはできましたが、FastAPIはまだそれを認識していません。これを修正するには、main.pyでstaticディレクトリを「マウント」する必要があります。
main.pyを開き、次のように変更します。
# main.py from fastapi import FastAPI, Request from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse import markdown from fastapi.staticfiles import StaticFiles # 1. StaticFilesをインポート app = FastAPI() # 2. 静的ディレクトリをマウント # これは、/staticで始まるリクエストが # "static"フォルダ内のファイルを探すべきであることをFastAPIに伝えます app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") # --- ホームルート(変更なし) --- @app.get("/", response_class=HTMLResponse) async def root(request: Request): context = { "request": request, "page_title": "Hello, Jinja2!" } return templates.TemplateResponse("index.html", context) # --- Docルート(次に変更) --- @app.get("/docs/hello", response_class=HTMLResponse) async def get_hello_doc(request: Request): md_file_path = "docs/hello.md" try: with open(md_file_path, "r", encoding="utf-8") as f: md_content = f.read() except FileNotFoundError: return HTMLResponse(content="<h1>404 - Document Not Found</h1>", status_code=404) # 現在は未変更、次のステップで変更します html_content = markdown.markdown(md_content) context = { "request": request, "page_title": "Hello, Markdown!", "content": html_content } return templates.TemplateResponse("doc.html", context)
ステップ5:HTMLテンプレートにCSSを含める
templates/doc.htmlを変更し、HTMLページがコードハイライトCSSをロードできるように、<head>に<link>タグを追加します。
<!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> <link rel="stylesheet" href="{{ url_for('static', path='css/highlight.css') }}" /> </head> <body> <h1>{{ page_title }}</h1> <hr /> <div class="doc-content">{{ content | safe }}</div> </body> </html>
ステップ6:Markdown拡張機能の有効化
最後のステップとして、main.pyに戻り、python-markdownライブラリにcodehilite拡張機能を使用するように指示しましょう。
また、バッククォート3つ(```)内のコンテンツをコードブロックとしてレンダリングするために使用されるfenced_code拡張機能も必要です。これは、構文ハイライトを表示するための必須条件です。
main.pyのget_hello_docルート関数を変更します。
# main.py # ... (他のインポートとappインスタンスは変更なし) ... # ... (app.mountとtemplatesインスタンスは変更なし) ... # ... (rootホームルートは変更なし) ... @app.get("/docs/hello", response_class=HTMLResponse) def get_hello_doc(request: Request): """ hello.mdドキュメントを読み込み、解析、レンダリングします(構文ハイライト付き) """ md_file_path = "docs/hello.md" try: with open(md_file_path, "r", encoding="utf-8") as f: md_content = f.read() except FileNotFoundError: return HTMLResponse(content="<h1>404 - Document Not Found</h1>", status_code=404) # 主な変更点:Markdown拡張機能を有効にする # 1. fenced_code: ``` 構文をサポート # 2. codehilite: 構文ハイライトを有効にする(Pygmentsを自動的に使用) extensions = ['fenced_code', 'codehilite'] html_content = markdown.markdown(md_content, extensions=extensions) context = { "request": request, "page_title": "Hello, Markdown!", "content": html_content } return templates.TemplateResponse("doc.html", context)
ステップ7:実行とテスト
uvicorn main:app --reloadを実行してサーバーを起動します。
再度http://127.0.0.1:8000/docs/helloにアクセスします。
hello.mdのPythonコードブロックに、monokaiテーマの構文ハイライトが適用されているのがわかります。CSSを生成する際のパラメータを変更して、他のハイライトテーマの効果を確認することもできます。

プロジェクトのオンラインデプロイ
ドキュメントサイトは皆が閲覧できるようにするものなので、ローカルで実行するだけでは十分ではありません。次に、オンラインでデプロイしましょう。
簡単なデプロイオプションはLeapcellを使用することです。これは、FastAPIを含むさまざまな言語やフレームワークのプロジェクトをホストできるWebアプリホスティングプラットフォームです。
以下の手順に従ってください。
1.ウェブサイトでアカウントを登録します。
2.プロジェクトをGitHubにコミットします。手順については、GitHubの公式ドキュメントを参照してください。Leapcellは後でGitHubリポジトリからコードを取得します。
3.Leapcellページで「Create Service」をクリックします。

4.FastAPIリポジトリを選択すると、Leapcellが必要な構成を自動入力しているのが表示されます。

5.一番下の「Submit」をクリックしてデプロイします。デプロイはすぐに完了し、デプロイホームページに戻されます。ここで、Leapcellがドメインを提供していることがわかります。これはブログのオンラインアドレスです。

結論と次のステップ
構文ハイライトにより、ドキュメントサイトははるかにプロフェッショナルに見えます。
しかし、まだ問題が1つあります。doc.htmlテンプレートのページタイトル({{ page_title }})は、main.pyのルート関数でハードコードされたままです("page_title": "Hello, Markdown!")。これはMarkdownファイルから取得されているわけではありません。
これは非常に柔軟性がありません。正しいアプローチは、このタイトルもhello.mdファイル自体から読み取ることです。
次の記事では、MarkdownファイルでFrontmatter(title、dateなどの記事メタデータを定義するために使用)を導入し、プロジェクトでFrontmatterの解析と読み込みを実装して、コードでのハードコーディングを回避します。

