Docusaurus Benzeri FastAPI Sitesinde Dinamik Sidebar Oluşturma Rehberi
Modern dokümantasyon siteleri, kullanıcıların aradıkları bilgiye hızlıca ulaşmalarını sağlayan, iyi düzenlenmiş ve etkileşimli sidebar (kenar çubuğu) navigasyonuna sahiptir. Docusaurus gibi popüler statik site üreticileri bu konuda standartları belirlerken, FastAPI ile dinamik bir web sitesi geliştirirken benzer bir deneyimi nasıl sunabiliriz? Bu makalede, FastAPI ve Jinja2 kullanarak, dosya sistemi tabanlı veya yapılandırma dosyaları üzerinden dinamik olarak oluşturulan, Docusaurus benzeri bir sidebar yapısını adım adım inşa edeceğiz. Bu sayede, projenizin dokümantasyonunu yönetmek ve kullanıcılarınıza kesintisiz bir gezinme deneyimi sunmak artık çok daha kolay olacak. Hazır mısınız?
Günümüzün hızla değişen yazılım dünyasında, projelerin dokümantasyonu da sürekli güncellenme ihtiyacı hisseder. Manuel olarak yönetilen statik HTML sayfalarındaki navigasyon menüleri, her değişiklikte tek tek güncellenmesi gereken bir kabusa dönüşebilir. Özellikle büyük ve çok sayıda sayfa içeren dokümantasyon sitelerinde, bu durum geliştiriciler için ciddi bir zaman kaybı ve hata kaynağıdır. İşte bu noktada dinamik bir sidebar çözümü devreye girer. Peki, bu dinamik yaklaşım bize ne gibi avantajlar sunar?
Öncelikle, bakım kolaylığı en büyük artıdır. Yeni bir doküman sayfası eklediğinizde veya mevcut bir sayfayı sildiğinizde, sidebar’ınız otomatik olarak güncellenir. Bu, geliştiricilerin sadece içerik oluşturmaya odaklanmasını sağlar ve navigasyon elementleriyle uğraşma yükünü ortadan kaldırır. Bu durum, özellikle çevik geliştirme metodolojileri uygulayan ekipler için hayati önem taşır; zira içerik ve yapılandırma arasındaki ayrım netleşir, geliştirme süreçleri hızlanır.
İkinci olarak, tutarlılık sağlar. Tüm dokümantasyon sayfalarınızda aynı navigasyon yapısının kullanılması, kullanıcı deneyimi açısından kritik öneme sahiptir. Dinamik sidebar, tutarlı bir gezinme hiyerarşisi oluşturarak kullanıcıların sitenizde kaybolmasını engeller ve bilgiyi daha kolay bulmalarına yardımcı olur. Ayrıca, markanızın veya projenizin genel görsel kimliğini yansıtan tek tip bir arayüz sunar.
Üçüncü bir avantaj ise ölçeklenebilirliktir. Projeniz büyüdükçe ve dokümantasyonunuz genişledikçe, dinamik bir sistem bu büyümeye rahatlıkla ayak uydurabilir. Yüzlerce sayfa barındıran kompleks dokümantasyon siteleri bile, iyi tasarlanmış dinamik bir yapıyla kolayca yönetilebilir. Bu durum, uzun vadeli projeler için olmazsa olmaz bir özelliktir. Ek olarak, içerik yönetimi için farklı kaynaklardan (veritabanı, API’ler, farklı dosya türleri) veri çekme yeteneği sunar, bu da statik çözümlerle mümkün olmayan bir esneklik sağlar.
Son olarak, esneklik ve özelleştirme imkanları sunar. Sidebar’ınızın görünümünü ve davranışını merkezi bir yerden, genellikle bir yapılandırma dosyası (YAML veya JSON) aracılığıyla veya dosya sisteminin kendisini tarayarak kolayca değiştirebilirsiniz. Bu sayede, farklı kullanıcı grupları veya farklı bağlamlar için özelleştirilmiş navigasyon menüleri oluşturmak mümkün hale gelir. Örneğin, belirli rollerdeki kullanıcılar için farklı menü öğeleri gösterebilir veya projenin farklı versiyonlarına özel navigasyonlar sunabilirsiniz. Bu tür dinamik özelleştirmeler, kullanıcı deneyimini zenginleştirirken aynı zamanda geliştiricilere de büyük bir kontrol sağlar.
Peki, Docusaurus gibi araçlar bunu zaten yaparken, neden FastAPI ile kendi çözümümüzü inşa etmeliyiz? Docusaurus harika bir araçtır, ancak bazen daha fazla esnekliğe, özel bir arka uç entegrasyonuna veya sadece Python ekosistemi içinde kalma isteğine ihtiyacımız olabilir. Özellikle dokümantasyonunuz bir web uygulamasıyla iç içe geçmişse veya içeriği dinamik olarak bir veritabanından çekmek gibi daha karmaşık gereksinimleriniz varsa, FastAPI ile kendi çözümünüzü oluşturmak size tam kontrol sağlayacaktır. Bu yaklaşım, dokümantasyonunuzu bir statik site sınırlarının ötesine taşıyarak, API entegrasyonları, kullanıcı kimlik doğrulaması veya kişiselleştirilmiş içerik sunumu gibi özelliklerle zenginleştirme potansiyeli sunar. Kısacası, kontrolün tamamen sizde olduğu, projenizin özel ihtiyaçlarına göre şekillenebilen bir çözüm inşa etmek istiyorsanız, doğru yerdesiniz.
Sidebar Oluşturma Sürecinin Temel Kavramları ve Mimarisi
FastAPI ile Docusaurus benzeri bir sidebar oluşturmak, aslında birkaç temel teknolojinin ve yaklaşımın birleşimini gerektirir. Bu süreçte neler kullanacağımızı ve genel mimarinin nasıl işleyeceğini anlamak, adımları daha kolay takip etmenizi sağlayacaktır. Temelde, bir “yapılandırma” katmanı, bir “işleme” katmanı ve bir “sunum” katmanından bahsedebiliriz.
Yapılandırma Katmanı: İçeriğimizi Nasıl Tanımlarız?
Docusaurus gibi sistemler genellikle Markdown dosyalarını ve bunların dizin yapısını otomatik olarak algılayarak veya _category_.json, sidebar.js gibi yapılandırma dosyalarıyla çalışır. Bizim de benzer bir yaklaşıma ihtiyacımız var. İçeriğimizin (dokümanlarımızın) hangi sırayla ve hangi başlıklar altında sidebar’da görüneceğini belirlemeliyiz. Bunun için iki yaygın yöntem izlenebilir:
- Dosya Sistemi Tabanlı Yapılandırma: Dokümanlarınızı belirli bir klasör yapısı içinde tutarsınız (örneğin,
docs/getting_started/introduction.md,docs/features/feature_x.md). FastAPI uygulamamız, bu klasör yapısını tarar ve her bir Markdown dosyasını bir sidebar öğesi olarak algılar. Klasör isimleri kategorileri, dosya isimleri ise sayfaları temsil edebilir. Bu yöntem, özellikle dosya tabanlı bir içerik yönetimi tercih edenler için oldukça pratik ve sürdürülebilirdir. Ancak, öğelerin sırasını veya başlıklarını dosya adlarından bağımsız olarak kontrol etmek istediğinizde sınırlayıcı olabilir. - Harici Yapılandırma Dosyası (YAML/JSON): Sidebar’ınızın tüm yapısını (kategoriler, alt kategoriler, sayfalar ve bunların sırası) bir YAML veya JSON dosyası içinde açıkça tanımlarsınız (örneğin,
sidebar.yaml). Bu dosya, uygulamanız tarafından okunur ve sidebar bu tanıma göre inşa edilir. Bu yöntem, dosya sistemi yapısından bağımsız olarak daha esnek bir sıralama ve başlıklandırma kontrolü sağlar. Ayrıca, tek bir merkezden sidebar’ın tüm görünümünü ve içeriğini yönetme imkanı sunar.
Biz bu makalede ikinci yönteme, yani harici bir YAML yapılandırma dosyasına odaklanacağız çünkü bu, daha esnek ve Docusaurus’un “sidebar.js” mantığına daha yakın bir kontrol sunar. YAML, hem okunabilirliği hem de hiyerarşik veri tanımlama yeteneği sayesinde bu tür yapılandırmalar için idealdir.
İşleme Katmanı: FastAPI ve Python’ın Gücü
Yapılandırma dosyamızı veya dosya sistemimizi okuduktan sonra, bu ham veriyi HTML’e dönüştürebileceğimiz anlamlı bir Python veri yapısına çevirmemiz gerekir. İşte burada FastAPI’nin gücünü kullanacağız:
- Dosya Okuma ve Ayrıştırma: FastAPI uygulamamız,
sidebar.yamldosyasını okuyacak ve içeriğini Python objelerine (listeler, dictionary’ler) dönüştürecek. Python’ınyamlkütüphanesi bu işlem için biçilmiş kaftandır. - Veri Yapısının Oluşturulması: Okunan yapılandırmaya göre, her bir sidebar öğesi için uygun bir veri modeli (örneğin,
SidebarItemadında bir Pydantic modeli) oluşturabiliriz. Bu modeller, öğenin başlığını, URL’sini, alt öğelerini (eğer varsa) ve diğer metadata’sını içerecektir. Bu adım, hem veriyi düzenli tutar hem de tip güvenliği sağlar. - İçerik Yönetimi (Markdown): Sidebar öğelerinin işaret ettiği gerçek doküman içerikleri genellikle Markdown dosyalarıdır. Uygulamamız, bu Markdown dosyalarını okumalı ve HTML’e çevirmelidir.
python-markdownveyamarkdown-it-pygibi kütüphaneler bu dönüşüm için kullanılabilir. Bu sayede, yazarlar sadece Markdown ile içerik oluştururken, web arayüzünde bu içerik otomatik olarak zengin HTML’e dönüştürülür.
Sunum Katmanı: Jinja2 ile HTML Oluşturma
FastAPI doğrudan HTML üretmek yerine, genellikle şablon motorlarıyla entegre çalışır. Jinja2, Python dünyasında en popüler ve güçlü şablon motorlarından biridir. İşte nasıl kullanacağımız:
- Şablon Yükleme: FastAPI, Jinja2 şablonlarını belirli bir klasörden (örneğin,
templates) yükleyecek şekilde yapılandırılabilir. - Dinamik HTML Oluşturma: İşleme katmanında hazırladığımız Python veri yapısını (sidebar menü öğeleri ve doküman içeriği), bir Jinja2 şablonuna (örneğin,
base.htmlveyadocs.html) parametre olarak göndereceğiz. - Sidebar’ın Render Edilmesi: Jinja2 şablonu, bu veri yapısını kullanarak dinamik olarak bir
veetiketlerinden oluşan bir sidebar menüsü oluşturacak. Bu şablon içinde döngüler (forloop), koşullu ifadeler (ifstatement) ve değişkenler kullanılarak kompleks menü yapıları inşa edilebilir. Ayrıca, ana içerik alanı için de Markdown’dan dönüştürülen HTML içeriği buraya yerleştirilecektir. Bu sayede, tüm sayfanın HTML yapısı tek bir yerden yönetilirken, sidebar ve içerik dinamik olarak değişebilir.
Bu üç katmanı bir araya getirerek, sadece birkaç dosya değişikliğiyle otomatik olarak güncellenen, güçlü ve esnek bir dokümantasyon sitesi navigasyonu oluşturabiliriz. Bu mimari, modülerliği ve ayrımı destekleyerek projenizi daha sürdürülebilir hale getirir. Şimdi bu teorik bilgiyi pratiğe dökme zamanı!
Adım 1: Proje Yapısını Oluşturma ve Gerekli Kütüphaneleri Kurma
Her büyük projenin temelinde sağlam bir yapılandırma yatar. Docusaurus benzeri bir dokümantasyon sitesi oluştururken de benzer bir yaklaşıma ihtiyacımız var. İlk adım olarak, projemizin temel dizin yapısını oluşturalım ve gerekli Python kütüphanelerini kuralım. Bu adım, daha sonraki geliştirmeler için düzenli ve anlaşılır bir temel sağlayacaktır.
Proje Dizini Yapısı
Aşağıdaki gibi bir dizin yapısı, hem dokümantasyon içeriğimizi hem de FastAPI uygulamamızın kodunu düzenli bir şekilde barındırmamızı sağlar:
fastapi-docs-site/
├── docs/
│ ├── getting-started/
│ │ ├── introduction.md
│ │ └── installation.md
│ ├── features/
│ │ ├── feature-x.md
│ │ └── feature-y.md
│ └── _category_.yaml # İsteğe bağlı, kategoriler için metadata
├── templates/
│ ├── base.html
│ └── doc_page.html
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── main.js
├── sidebar.yaml
├── main.py
├── requirements.txt
└── .gitignore
Bu yapıyı biraz açalım:
fastapi-docs-site/: Projemizin ana dizini.docs/: Tüm Markdown dokümanlarımızın bulunduğu ana klasör. İçindeki alt klasörler (örneğin,getting-started,features) doğrudan sidebar kategorilerini temsil edebilir veya yapılandırma dosyamızda referans göstereceğimiz gruplamaları oluşturur. Her bir.mddosyası, tek bir doküman sayfasına karşılık gelir.templates/: Jinja2 şablon dosyalarımızın yer alacağı dizin.base.htmlgenel sayfa yapımızı (header, footer, sidebar konteyneri vb.) tanımlarken,doc_page.htmlspesifik olarak doküman içeriğinin nasıl yerleştirileceğini vebase.html'i nasıl genişleteceğini belirler.static/: CSS, JavaScript ve resimler gibi statik dosyalarımızın bulunduğu dizin. Bu dosyalar doğrudan tarayıcı tarafından erişilebilir olacaktır.sidebar.yaml: Sidebar'ımızın yapısını ve içeriğini tanımlayacağımız YAML yapılandırma dosyası. Bu dosya,docs/klasöründeki içeriğin nasıl gruplandırılacağını ve hangi sırada gösterileceğini belirleyecek.main.py: FastAPI uygulamamızın ana kodu, rotaları ve mantığı burada yer alacak.requirements.txt: Projemizin bağımlılıklarını listeleyen dosya..gitignore: Git versiyon kontrol sistemi tarafından görmezden gelinecek dosyaları belirler (örneğin, sanal ortamlar, geçici dosyalar).
Gerekli Kütüphaneleri Kurma
Projemizin çalışabilmesi için birkaç temel Python kütüphanesine ihtiyacımız var. Bu kütüphaneler, FastAPI uygulamasını çalıştırmak, Jinja2 şablonlarını işlemek, YAML yapılandırmalarını okumak ve Markdown içeriğini HTML'e dönüştürmek için kullanılacak. requirements.txt dosyanızı aşağıdaki gibi oluşturabilirsiniz:
fastapi==0.111.0
uvicorn==0.29.0
Jinja2==3.1.4
PyYAML==6.0.1
markdown==3.6
python-multipart==0.0.9 # FastAPI'nin form verilerini işlemesi için gerekli olabilir
Daha sonra, bu bağımlılıkları bir sanal ortamda (virtual environment) kurmak en iyi uygulamadır. Böylece projenizin bağımlılıkları sisteminizdeki diğer Python projelerinden izole edilmiş olur:
# Sanal ortam oluşturma
python -m venv venv
# Sanal ortamı etkinleştirme (Linux/macOS)
source venv/bin/activate
# Sanal ortamı etkinleştirme (Windows)
.\venv\Scripts\activate
# Bağımlılıkları kurma
pip install -r requirements.txt
Bu komutlar, projeniz için gerekli tüm araçları yükleyecek ve geliştirme ortamınızı hazırlayacaktır. Artık temel altyapımız hazır olduğuna göre, bir sonraki adımda sidebar yapılandırma dosyamızı oluşturmaya geçebiliriz. Bu düzenli kurulum, gelecekteki olası sorunları minimize eder ve projenizi daha yönetilebilir kılar. Bu sayede, içerik geliştirmeye ve uygulamanın mantığına daha fazla odaklanabilirsiniz.
Adım 2: Sidebar Yapılandırmasını Tanımlama (sidebar.yaml ile)
Dinamik sidebar'ımızın kalbi, onun nasıl görüneceğini ve hangi sayfaları içereceğini belirleyen yapılandırma dosyasıdır. Docusaurus'un sidebar.js dosyalarına benzer şekilde, biz de bir sidebar.yaml dosyası kullanarak bu yapıyı tanımlayacağız. YAML, hiyerarşik verileri insan tarafından okunabilir bir formatta ifade etmek için idealdir ve bu nedenle dokümantasyon yapılandırması için mükemmel bir seçimdir.
sidebar.yaml Dosyasının Yapısı
sidebar.yaml dosyasının, dokümanlarınızın mantıksal gruplamalarını ve bu gruplar içindeki sayfaların sırasını net bir şekilde belirtmesi gerekmektedir. İşte örnek bir sidebar.yaml içeriği:
- type: category
label: Başlangıç
collapsible: true
collapsed: false
items:
- type: doc
id: getting-started/introduction
label: Giriş
- type: doc
id: getting-started/installation
label: Kurulum
- type: doc
id: getting-started/first-steps
label: İlk Adımlar
- type: category
label: Özellikler
collapsible: true
collapsed: true
items:
- type: doc
id: features/feature-x
label: X Özelliği
- type: doc
id: features/feature-y
label: Y Özelliği
- type: link
label: Harici Bağlantı
href: https://fastapi.tiangolo.com/
target: _blank
- type: doc
id: legal/privacy-policy
label: Gizlilik Politikası
Bu yapılandırma dosyasını daha yakından inceleyelim:
- Her bir ana öğe, bir kategori, doküman veya harici bir bağlantı olabilir. Bu,
typeanahtarıyla belirtilir. type: category: Bir grup dokümanı temsil eder.label: Sidebar'da görünecek kategori başlığı.collapsible: (İsteğe bağlı, varsayılantrue) Kategorinin genişletilebilir/daraltılabilir olup olmadığını belirler.collapsed: (İsteğe bağlı, varsayılantrue) Sayfa yüklendiğinde kategorinin varsayılan olarak daraltılmış mı (true) yoksa açık mı (false) olacağını belirtir.items: Bu kategoriye ait olan dokümanları veya alt kategorileri içeren bir liste.
type: doc: Tek bir doküman sayfasını temsil eder.id: Doküman dosyasınındocs/klasörü içindeki yolunu belirtir, ancak.mduzantısı olmadan. Örneğin,getting-started/introductionID'si,docs/getting-started/introduction.mddosyasına karşılık gelir. Bu ID aynı zamanda URL yolu olarak da kullanılacaktır (örn./docs/getting-started/introduction).label: Sidebar'da görünecek doküman başlığı.
type: link: Harici bir web sitesine yönlendiren bir bağlantıyı temsil eder.label: Sidebar'da görünecek bağlantı başlığı.href: Bağlantının yönlendireceği URL.target: (İsteğe bağlı) Bağlantının yeni bir sekmede açılıp açılmayacağını belirler (örneğin,_blank).
Doküman İçerik Dosyalarını Oluşturma
Yukarıdaki sidebar.yaml dosyasıyla eşleşecek şekilde, docs/ klasörünüzde bazı Markdown dosyaları oluşturmalısınız. Örneğin:
docs/getting-started/introduction.md
# Hoş Geldiniz!
Bu, projemizin başlangıç dokümantasyonudur. Amacımız, sizi hızlıca alıştırmak ve projenin temel prensiplerini anlamanızı sağlamaktır.
Projemiz Hakkında
Bu proje, modern web uygulamaları geliştirmek için tasarlanmış bir araç setidir.
İlgili Bağlantılar:
- [Kurulum](/docs/getting-started/installation)
docs/getting-started/installation.md
# Kurulum Rehberi Projemizi bilgisayarınıza kurmak için aşağıdaki adımları izleyin: 1. Python 3.9 veya üzeri yüklü olduğundan emin olun. 2. Proje dizinine gidin ve bağımlılıkları yükleyin:pip install -r requirements.txt3. Uygulamayı başlatın:uvicorn main:app --reload
Gereksinimler
- Python 3.9+
- FastAPI
- Uvicorn
Bu
.mddosyalarının her biri,sidebar.yaml'dekiidalanlarıyla eşleşen yollarda yer almaktadır. Markdown dosyalarınızın başlıkları (örneğin,# Hoş Geldiniz!) genellikle sayfa içinde görünürken, sidebar'da gösterilecek başlıklarsidebar.yaml'dekilabelalanından gelecektir. Bu ayrım, hem SEO dostu URL'ler oluşturmanıza hem de kullanıcı dostu menü başlıkları kullanmanıza olanak tanır.Bu yapılandırma, dokümantasyonunuzun hem statik yapısını hem de dinamik gezinme mantığını belirler. Bir sonraki adımda, FastAPI uygulamamızın bu YAML dosyasını nasıl okuyacağını ve işleyeceğini inceleyeceğiz. Bu adım, statik yapılandırmayı dinamik bir Python nesnesine dönüştürmenin ve web uygulamamızın anlayabileceği bir formata getirmenin temelini oluşturur.
Adım 3: FastAPI ile Yapılandırmayı Okuma ve İşleme
Şimdi sıra, oluşturduğumuz
sidebar.yamldosyasını FastAPI uygulamamız içinde kullanışlı bir veri yapısına dönüştürmeye geldi. Bu adımda Python'ınyamlkütüphanesini kullanarak dosyayı okuyacak, ardından bu veriyi FastAPI'nin Jinja2 şablonlarına aktarabileceği bir formata getireceğiz. Ayrıca, Markdown dokümanlarını HTML'e çevirme işlevini de bu bölümde ele alacağız.YAML Yapılandırmasını Yükleme Fonksiyonu
Öncelikle,
main.pydosyanıza YAML dosyasını okuyacak bir yardımcı fonksiyon ekleyelim:# main.py import yaml from pathlib import Path from markdown import markdown from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates # Projenin kök dizini BASE_DIR = Path(__file__).resolve().parent # YAML dosyasını yükleme fonksiyonu def load_sidebar_config(config_path: Path): with open(config_path, 'r', encoding='utf-8') as file: return yaml.safe_load(file) # Markdown'ı HTML'e çevirme fonksiyonu def convert_markdown_to_html(md_path: Path) -> str: with open(md_path, 'r', encoding='utf-8') as file: md_content = file.read() return markdown(md_content, extensions=['fenced_code', 'tables', 'attr_list']) # FastAPI uygulamasını başlat app = FastAPI() # Statik dosyaları bağla (CSS, JS vb.) app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") # Jinja2 şablonlarını yükle templates = Jinja2Templates(directory=BASE_DIR / "templates") # Sidebar yapılandırmasını yükle try: sidebar_config = load_sidebar_config(BASE_DIR / "sidebar.yaml") except FileNotFoundError: print("Hata: sidebar.yaml dosyası bulunamadı. Lütfen kontrol edin.") sidebar_config = [] # Boş liste ile devam et except Exception as e: print(f"Hata: sidebar.yaml yüklenirken bir sorun oluştu: {e}") sidebar_config = []Bu kod bloğunda:
BASE_DIR: Projenizin kök dizinini belirler. Bu, tüm dosya yollarının göreceli olarak yönetilmesini kolaylaştırır.load_sidebar_config: Verilen yoldaki YAML dosyasını okur ve Python'ın sözlük/liste yapısına dönüştürür. Hata yönetimi de eklenmiştir.convert_markdown_to_html: Belirtilen yoldaki Markdown dosyasını okur vemarkdownkütüphanesi kullanarak HTML'e dönüştürür.extensionsparametresi, daha zengin Markdown özelliklerini (kod blokları, tablolar vb.) desteklemek için önemlidir.app = FastAPI(): Uygulamamızı başlatır.app.mount("/static", ...):static/klasörümüzdeki CSS ve JS gibi dosyaları/staticURL yolu üzerinden erişilebilir kılar.templates = Jinja2Templates(...): Jinja2 şablon motorumuzutemplates/klasörümüzü işaret ederek başlatır.sidebar_config = load_sidebar_config(...): Uygulama başlatılırkensidebar.yamldosyasını yükler vesidebar_configdeğişkeninde tutarız. Bu sayede her istekte dosyayı okumak zorunda kalmayız.
Doküman Sayfaları için FastAPI Rotası
Şimdi, /docs/ prefix'i altındaki tüm doküman sayfalarını dinamik olarak işleyecek bir FastAPI rotası tanımlayalım. Bu rota, URL'deki doküman ID'sini alacak, ilgili Markdown dosyasını bulacak, onu HTML'e çevirecek ve Jinja2 şablonuna gönderecektir.
# main.py dosyasına ekleyin
@app.get("/docs/{doc_id:path}", response_class=HTMLResponse)
async def read_doc(request: Request, doc_id: str):
"""
Dinamik olarak doküman sayfalarını yükler ve render eder.
doc_id: URL'deki doküman yolu (örn. getting-started/introduction)
"""
doc_path = BASE_DIR / "docs" / f"{doc_id}.md"
if not doc_path.is_file():
# Eğer dosya yoksa 404 hatası döndür
return templates.TemplateResponse(
"404.html",
{"request": request, "sidebar_config": sidebar_config, "title": "Sayfa Bulunamadı"},
status_code=404
)
# Markdown içeriğini HTML'e çevir
doc_content_html = convert_markdown_to_html(doc_path)
# Sayfa başlığını Markdown dosyasının ilk H1 başlığından veya doc_id'den al
# Daha sofistike bir başlık çekme mekanizması kurulabilir.
# Şimdilik, sadece doc_id'yi kullanabiliriz veya Markdown içeriğinden ilk H1'i parse edebiliriz.
first_h1_match = next((line for line in doc_content_html.split('\n') if line.strip().startswith('')), None)
page_title = doc_id.replace('-', ' ').replace('/', ' ').title() # Varsayılan başlık
if first_h1_match:
# ve
etiketlerini temizle
page_title = first_h1_match.replace("", "").replace("
", "").strip()
# Jinja2 şablonunu render et
return templates.TemplateResponse(
"doc_page.html",
{
"request": request,
"sidebar_config": sidebar_config,
"doc_content": doc_content_html,
"current_doc_id": doc_id, # Aktif sayfayı işaretlemek için
"title": page_title
}
)
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
"""
Ana sayfa yönlendirmesi veya varsayılan bir başlangıç sayfası.
"""
# İsteğe bağlı olarak, ana sayfadan direkt ilk dokümana yönlendirebilirsiniz.
if sidebar_config and sidebar_config[0].get('type') == 'category' and sidebar_config[0].get('items'):
first_doc_id = sidebar_config[0]['items'][0]['id']
return templates.TemplateResponse(
"doc_page.html",
{
"request": request,
"sidebar_config": sidebar_config,
"doc_content": convert_markdown_to_html(BASE_DIR / "docs" / f"{first_doc_id}.md"),
"current_doc_id": first_doc_id,
"title": sidebar_config[0]['items'][0]['label']
}
)
return templates.TemplateResponse(
"home.html", # Eğer home.html yoksa, doc_page.html'i kullanıp boş içerik geçebilirsiniz
{"request": request, "sidebar_config": sidebar_config, "title": "Ana Sayfa", "doc_content": "Hoş Geldiniz!
Lütfen soldaki menüden bir doküman seçin.
"}
)
Bu rotada yapılanlar:
@app.get("/docs/{doc_id:path}", ...):/docs/ile başlayan ve sonrasında herhangi bir yola sahip olan tüm istekleri yakalar.{doc_id:path}kısmı,/karakterlerini de içeren bir yoludoc_iddeğişkenine atamamızı sağlar.doc_path = BASE_DIR / "docs" / f"{doc_id}.md": Gelendoc_idiledocs/klasöründeki ilgili Markdown dosyasının tam yolunu oluşturur.if not doc_path.is_file(): Dosyanın gerçekten var olup olmadığını kontrol eder. Yoksa, bir 404 (Sayfa Bulunamadı) şablonu döndürülür.doc_content_html = convert_markdown_to_html(doc_path): Markdown dosyasını HTML'e çevirir.page_title = ...: Sayfa başlığını, URL'den veya Markdown içeriğindeki ilk H1 etiketinden dinamik olarak alır. Bu, tarayıcı sekmesinde veya sayfa içinde doğru başlığın görüntülenmesi için önemlidir.return templates.TemplateResponse(...):doc_page.htmlşablonunu render ederken,requestobjesini,sidebar_config(menü için),doc_content(ana içerik için),current_doc_id(aktif menü öğesini işaretlemek için) vetitledeğişkenlerini şablona gönderir.@app.get("/"): Ana sayfa isteğini karşılar. Varsayılan olarak ilk dokümanı yüklemeyi veya basit bir karşılama sayfası göstermeyi seçebilirsiniz.
Bu adımlarla, FastAPI uygulamamız artık YAML yapılandırmasını okuyabiliyor ve doküman içeriğini işleyip HTML'e çevirebiliyor. Sırada, bu veriyi kullanarak dinamik sidebar'ımızı HTML şablonlarında nasıl oluşturacağımız var.
Adım 4: Jinja2 Şablonları ile Sidebar'ı Oluşturma
Şimdiye kadar, FastAPI uygulamamız sidebar.yaml dosyamızı okuyabiliyor ve Markdown içeriğini HTML'e çevirebiliyor. Ancak bu veriyi bir web arayüzüne taşımamız gerekiyor. İşte bu noktada Jinja2 şablonları devreye giriyor. templates/ klasörümüzde base.html ve doc_page.html olmak üzere iki ana şablon oluşturacağız. Bu şablonlar, uygulamanın genel yapısını ve sidebar'ın render edilme mantığını içerecek.
templates/base.html (Temel Sayfa Yapısı)
Bu şablon, HTML sayfamızın temel iskeletini, etiketini, genel CSS bağlantılarını ve ana layout elementlerini (header, footer, ana içerik alanı ve sidebar'ın yer tutucusu) tanımlar. Diğer şablonlar bu dosyayı genişleterek kendi içeriklerini yerleştireceklerdir.
{{ title }} | Proje Dokümantasyon
{% block content %}{% endblock %}
Yukarıdaki base.html şablonunda dikkat etmemiz gerekenler:
{{ title }}: FastAPI'den gelen sayfa başlığı buraya yerleştirilir.url_for('static', path='/css/style.css'): FastAPI'ninStaticFilesmount'u üzerinden CSS dosyasına dinamik olarak yol oluşturur.,: SEO için önemli meta etiketleridir.: Sidebar menümüzü barındıran ana eleman.
{% for item in sidebar_config %}: FastAPI'den gelensidebar_configlistesi üzerinde döngü yaparız. Bu, her bir kategori, doküman veya harici bağlantı için HTML oluşturmamızı sağlar.{% if item.type == 'category' %}: Eğer öğe bir kategoriyse, genişletilebilir bir yapı oluştururuz. İç içe döngü ile kategori içindeki alt öğeleri (dokümanlar veya bağlantılar) işleriz.collapsedvecollapsiblebayrakları dinamik olarak CSS sınıflarını kontrol eder.{% if current_doc_id == sub_item.id %}: Aktif olarak görüntülenen doküman sayfasını sidebar'da işaretlemek için kullanılır, böylece kullanıcı nerede olduğunu bilir.url_for('read_doc', doc_id=sub_item.id): FastAPI rotamıza (read_doc) dinamik URL oluşturur.{% block content %}{% endblock %}: Burası,base.html'i genişleten diğer şablonların kendi içeriklerini yerleştireceği ana alandır.
templates/doc_page.html (Doküman Sayfası Şablonu)
Bu şablon, base.html'i genişleterek ana doküman içeriğini (doc_content) content bloğuna yerleştirir.
{% extends "base.html" %}
{% block content %}
{{ doc_content | safe }}
{% endblock %}
Burada {{ doc_content | safe }} ifadesi önemlidir. Jinja2 varsayılan olarak HTML içeriğini kaçış karakterlerine dönüştürür (güvenlik için). Ancak Markdown'dan çevrilen içeriğimiz zaten güvenli bir HTML olduğu için, | safe filtresini kullanarak Jinja2'ye bu içeriği olduğu gibi render etmesini söyleriz.
templates/404.html (Sayfa Bulunamadı Şablonu)
Olmayan bir doküman sayfasına erişildiğinde gösterilecek basit bir hata sayfası:
{% extends "base.html" %}
{% block content %}
404 - Sayfa Bulunamadı
Aradığınız doküman sayfası bulunamadı. Lütfen URL'yi kontrol edin veya sidebar'dan başka bir sayfa seçin.
{% endblock %}
static/css/style.css (Temel Stil Tanımları)
Sidebar'ımızın ve genel layout'umuzun düzgün görünmesi için biraz CSS ekleyelim. Bu CSS aynı zamanda mobil uyumluluğu da göz önünde bulunduracaktır.
/* style.css */
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--bg-color: #f8f9fa;
--text-color: #343a40;
--sidebar-width: 280px;
--navbar-height: 60px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
}
.navbar {
background-color: var(--primary-color);
color: white;
padding: 0.8rem 1rem;
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 1000;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
height: var(--navbar-height);
display: flex;
align-items: center;
}
.navbar-brand {
color: white;
text-decoration: none;
font-size: 1.5rem;
font-weight: bold;
margin-left: 1rem;
}
.wrapper {
display: flex;
min-height: calc(100vh - var(--navbar-height)); /* Topbar yüksekliğini düş */
padding-top: var(--navbar-height); /* Topbar kadar boşluk bırak */
}
.sidebar {
width: var(--sidebar-width);
background-color: #ffffff;
border-right: 1px solid #e0e0e0;
padding: 1.5rem 0;
box-shadow: 2px 0 5px rgba(0,0,0,0.05);
position: fixed;
height: calc(100% - var(--navbar-height));
overflow-y: auto;
top: var(--navbar-height);
left: 0;
}
.sidebar-nav {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar-category {
margin-bottom: 0.5rem;
}
.category-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1.5rem;
cursor: pointer;
font-weight: bold;
color: var(--primary-color);
background-color: #e9ecef;
border-bottom: 1px solid #dee2e6;
}
.category-header:hover {
background-color: #e2e6ea;
}
.toggle-icon {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid var(--primary-color);
transition: transform 0.2s ease-in-out;
}
.sidebar-category.open .toggle-icon {
transform: rotate(180deg);
}
.category-items {
list-style: none;
padding: 0;
margin: 0;
overflow: hidden; /* Collapse animation */
transition: max-height 0.3s ease-out;
}
.category-items.collapsed {
max-height: 0;
}
.category-items:not(.collapsed) {
max-height: 500px; /* Büyük bir değer verin */
}
.sidebar-item a {
display: block;
padding: 0.75rem 1.5rem 0.75rem 2rem; /* Alt öğeleri biraz içeriden başlat */
color: var(--text-color);
text-decoration: none;
transition: background-color 0.2s, color 0.2s;
font-size: 0.95rem;
}
.sidebar-item a:hover {
background-color: #f0f0f0;
color: var(--primary-color);
}
.sidebar-item.active a {
background-color: var(--primary-color);
color: white;
font-weight: bold;
}
.content {
margin-left: var(--sidebar-width);
flex-grow: 1;
padding: 2rem;
max-width: 900px;
}
.doc-article h1, .doc-article h2, .doc-article h3, .doc-article h4, .doc-article h5, .doc-article h6 {
color: var(--primary-color);
margin-top: 2rem;
margin-bottom: 1rem;
}
.doc-article p {
margin-bottom: 1rem;
}
.doc-article pre {
background-color: #272727;
color: #f8f8f2;
padding: 1rem;
border-radius: 5px;
overflow-x: auto;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
margin-bottom: 1.5rem;
}
.doc-article code {
background-color: rgba(0,0,0,0.05);
padding: 0.2em 0.4em;
border-radius: 3px;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 0.9em;
}
.doc-article pre code {
background-color: transparent;
padding: 0;
border-radius: 0;
}
.doc-article table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.doc-article th, .doc-article td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.doc-article th {
background-color: #f2f2f2;
font-weight: bold;
}
.footer {
background-color: var(--secondary-color);
color: white;
text-align: center;
padding: 1rem;
font-size: 0.9rem;
margin-top: auto; /* Footer'ı alta sabitlemek için */
}
.error-page {
text-align: center;
padding: 5rem 2rem;
}
.expert-tip {
background-color: #fff3cd; /* Sarımsı arka plan */
border-left: 4px solid #ffc107; /* Sarı kenarlık */
padding: 1rem 1.5rem;
margin: 1.5rem 0;
color: #664d03; /* Koyu sarımsı metin */
border-radius: 4px;
font-style: italic;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* Mobil Uyumlu Tasarım */
@media (max-width: 992px) {
.sidebar {
width: 250px;
}
.content {
margin-left: 250px;
padding: 1rem;
}
}
@media (max-width: 768px) {
.wrapper {
flex-direction: column;
padding-top: calc(var(--navbar-height) + 1rem); /* Topbar ve biraz daha boşluk */
}
.sidebar {
position: static; /* Mobil'de sidebar'ı statik yap */
width: 100%;
height: auto;
border-right: none;
border-bottom: 1px solid #e0e0e0;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
padding: 0; /* İç dolguyu kaldır */
}
.content {
margin-left: 0;
padding: 1.5rem;
}
.category-items {
max-height: 0; /* Mobil'de varsayılan olarak daraltılmış */
}
.sidebar-category.open .category-items {
max-height: 500px; /* Açık olduğunda genişlet */
}
.category-header {
padding: 1rem;
font-size: 1.1rem;
}
.sidebar-item a {
padding: 0.75rem 1.5rem 0.75rem 1.5rem;
}
.navbar-brand {
font-size: 1.2rem;
}
}
static/js/main.js (Sidebar Etkileşimleri)
Kategorilerin daraltılıp genişletilmesi için basit bir JavaScript kodu ekleyelim:
// main.js
document.addEventListener('DOMContentLoaded', function() {
const categoryHeaders = document.querySelectorAll('.category-header');
categoryHeaders.forEach(header => {
header.addEventListener('click', function() {
const category = this.closest('.sidebar-category');
const categoryItems = category.querySelector('.category-items');
// Toggle 'open' class on the category
category.classList.toggle('open');
// Toggle 'collapsed' class on category items for CSS transition
categoryItems.classList.toggle('collapsed');
});
});
// Sayfa yüklendiğinde mevcut aktif kategoriyi aç
const activeItem = document.querySelector('.sidebar-item.active');
if (activeItem) {
let parentCategoryItems = activeItem.closest('.category-items');
while (parentCategoryItems) {
parentCategoryItems.classList.remove('collapsed');
let parentCategory = parentCategoryItems.closest('.sidebar-category');
if (parentCategory) {
parentCategory.classList.add('open');
}
parentCategoryItems = parentCategoryItems.parentElement.closest('.category-items');
}
}
});
Bu JS kodu, kategori başlıklarına tıklanıldığında ilgili category-items listesini genişletir veya daraltır. Ayrıca, sayfa yüklendiğinde aktif olan doküman öğesinin üst kategorilerini otomatik olarak açar, böylece kullanıcı nerede olduğunu kolayca görebilir. Bu basit JavaScript, Docusaurus benzeri kullanıcı deneyimini sağlamak için yeterli olacaktır.
Bu şablonlar ve stil dosyalarıyla, FastAPI uygulamamız artık dinamik olarak oluşturulan bir sidebar'a sahip, okunabilir ve modern görünümlü bir dokümantasyon sitesi sunabilir. Bir sonraki ve son adımda, bu parçaları bir araya getirip FastAPI uygulamasını nasıl başlatacağımıza ve çalıştıracağımıza bakacağız.
Adım 5: FastAPI Uygulamasını Entegre Etme ve Çalıştırma
Şimdiye kadar tüm gerekli bileşenleri hazırladık: Proje yapısını kurduk, sidebar.yaml ile navigasyonu tanımladık, Markdown dokümanlarımızı oluşturduk ve Jinja2 şablonlarımızla HTML çıktısı için zemini hazırladık. Artık main.py dosyamızda tüm bu parçaları bir araya getirerek FastAPI uygulamamızı çalışır hale getirebiliriz.
Aşağıda, main.py dosyasının son hali bulunmaktadır. Önceki adımlarda oluşturduğumuz fonksiyonları ve rotaları içerir.
# main.py
import yaml
from pathlib import Path
from markdown import markdown
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
# Projenin kök dizini
BASE_DIR = Path(__file__).resolve().parent
# YAML dosyasını yükleme fonksiyonu
def load_sidebar_config(config_path: Path):
try:
with open(config_path, 'r', encoding='utf-8') as file:
return yaml.safe_load(file)
except FileNotFoundError:
print(f"Hata: Yapılandırma dosyası bulunamadı: {config_path}")
return []
except yaml.YAMLError as e:
print(f"Hata: YAML ayrıştırma hatası: {e}")
return []
except Exception as e:
print(f"Hata: load_sidebar_config sırasında beklenmeyen hata: {e}")
return []
# Markdown'ı HTML'e çevirme fonksiyonu
def convert_markdown_to_html(md_path: Path) -> str:
try:
with open(md_path, 'r', encoding='utf-8') as file:
md_content = file.read()
# Kod blokları, tablolar ve özellik listeleri için uzantıları ekle
return markdown(md_content, extensions=['fenced_code', 'tables', 'attr_list', 'nl2br'])
except FileNotFoundError:
return "Doküman bulunamadı."
except Exception as e:
return f"Markdown dönüştürme hatası: {e}"
# FastAPI uygulamasını başlat
app = FastAPI(
title="Proje Dokümantasyon Sitesi",
description="FastAPI ile dinamik olarak oluşturulmuş dokümantasyon sayfası.",
version="1.0.0",
)
# Statik dosyaları bağla (CSS, JS vb.)
# Bu, tarayıcının /static/css/style.css gibi yollardan dosyalara erişmesini sağlar
app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static")
# Jinja2 şablonlarını yükle
templates = Jinja2Templates(directory=BASE_DIR / "templates")
# Sidebar yapılandırmasını uygulama başladığında bir kez yükle
sidebar_config = load_sidebar_config(BASE_DIR / "sidebar.yaml")
# --- Rotlar ---
@app.get("/docs/{doc_id:path}", response_class=HTMLResponse)
async def read_doc(request: Request, doc_id: str):
"""
Dinamik olarak doküman sayfalarını yükler ve render eder.
doc_id: URL'deki doküman yolu (örn. getting-started/introduction)
"""
doc_path = BASE_DIR / "docs" / f"{doc_id}.md"
if not doc_path.is_file():
# Eğer dosya yoksa 404 hatası döndür
return templates.TemplateResponse(
"404.html",
{"request": request, "sidebar_config": sidebar_config, "title": "Sayfa Bulunamadı"},
status_code=404
)
# Markdown içeriğini HTML'e çevir
doc_content_html = convert_markdown_to_html(doc_path)
# Sayfa başlığını Markdown dosyasının ilk H1 başlığından veya doc_id'den al
# Burada daha sağlam bir başlık çıkarma mekanizması kullanılabilir.
# Örneğin, Markdown metnini parse edip ilk H1'i bulmak gibi.
# Basitlik adına, şimdilik doc_id'den türetilmiş bir başlık veya doğrudan Markdown içeriğinden ilk H1.
page_title_candidate = doc_id.replace('-', ' ').replace('/', ' ').title()
# Markdown içeriğinden ilk H1'i çekmek için daha dikkatli bir yaklaşım
# Markdown çıktısı HTML olduğu için, HTML içindeki etiketini arayalım.
import re
h1_match = re.search(r'(.*?)', doc_content_html, re.IGNORECASE)
if h1_match:
page_title = h1_match.group(1).strip()
else:
page_title = page_title_candidate
# Jinja2 şablonunu render et
return templates.TemplateResponse(
"doc_page.html",
{
"request": request,
"sidebar_config": sidebar_config,
"doc_content": doc_content_html,
"current_doc_id": doc_id, # Aktif sayfayı işaretlemek için
"title": page_title
}
)
@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
"""
Ana sayfa yönlendirmesi veya varsayılan bir başlangıç sayfası.
"""
# Ana sayfayı direkt ilk dokümana yönlendirelim veya özel bir ana sayfa gösterebiliriz.
if sidebar_config and isinstance(sidebar_config, list) and sidebar_config:
first_doc_item = None
# Sidebar'daki ilk 'doc' tipindeki öğeyi bul
for item in sidebar_config:
if item.get('type') == 'doc':
first_doc_item = item
break
elif item.get('type') == 'category' and item.get('items'):
for sub_item in item['items']:
if sub_item.get('type') == 'doc':
first_doc_item = sub_item
break
if first_doc_item:
break
if first_doc_item:
first_doc_id = first_doc_item['id']
# İlk dokümanın içeriğini yükleyip ana sayfa olarak göster
return templates.TemplateResponse(
"doc_page.html",
{
"request": request,
"sidebar_config": sidebar_config,
"doc_content": convert_markdown_to_html(BASE_DIR / "docs" / f"{first_doc_id}.md"),
"current_doc_id": first_doc_id,
"title": first_doc_item.get('label', first_doc_id.replace('-', ' ').replace('/', ' ').title())
}
)
# Eğer sidebar boşsa veya ilk doküman bulunamazsa, genel bir karşılama sayfası göster
return templates.TemplateResponse(
"home.html", # Bu şablonu oluşturmanız gerekebilir veya doc_page.html kullanabilirsiniz
{
"request": request,
"sidebar_config": sidebar_config,
"title": "Hoş Geldiniz",
"doc_content": "Hoş Geldiniz!
Soldaki menüden bir doküman seçerek başlayın.
"
}
)
Uygulamayı Çalıştırma
Projenizi oluşturdunuz, kütüphanelerinizi kurdunuz ve tüm kodları yerleştirdiniz. Şimdi uygulamayı çalıştırmanın zamanı geldi. Terminalinizi açın, projenizin ana dizinine (fastapi-docs-site/) gidin ve aşağıdaki komutu çalıştırın:
uvicorn main:app --reload
Bu komut:
uvicorn: FastAPI uygulamalarını çalıştırmak için kullanılan ASGI sunucusudur.main:app:main.pydosyasındakiappisimli FastAPI uygulamasını ifade eder.--reload: Bu bayrak, kodunuzda her değişiklik yaptığınızda uygulamanın otomatik olarak yeniden yüklenmesini sağlar. Geliştirme sürecinde çok faydalıdır.
Uygulama başarıyla başlatıldığında, terminalinizde şuna benzer bir çıktı göreceksiniz:
INFO: Will watch for changes in these directories: ['/path/to/your/fastapi-docs-site']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [xxxxx]
INFO: Started server process [xxxxx]
INFO: Waiting for application startup.
INFO: Application startup complete.
Artık tarayıcınızı açıp http://127.0.0.1:8000 adresine giderek dinamik dokümantasyon sitenizi görebilirsiniz. Sidebar'ınızın sidebar.yaml dosyasında tanımladığınız gibi göründüğünü, linklere tıkladığınızda Markdown dosyalarınızın HTML'e çevrilerek yüklendiğini ve aktif sayfanın sidebar'da vurgulandığını fark edeceksiniz. Mobil cihazlarda sidebar'ın nasıl davranacağını görmek için tarayıcınızın geliştirici araçlarını kullanarak ekran boyutunu küçültebilirsiniz. CSS'deki @media sorguları sayesinde, daha küçük ekranlarda sidebar'ın farklı bir düzende (örneğin, üstte genişletilebilir menü olarak) göründüğünü göreceksiniz. Bu, kullanıcı deneyimini her boyuttaki ekran için optimize etmenize yardımcı olur.
İleri Seviye Optimizasyonlar ve Geliştirmeler
Artık temel dinamik sidebar yapımızı kurduğumuza göre, projemizi daha sağlam, performanslı ve kullanıcı dostu hale getirecek ileri seviye konulara değinelim. Bu optimizasyonlar, büyük ölçekli dokümantasyon siteleri veya özel gereksinimleri olan projeler için kritik öneme sahiptir.
1. Dinamik İçerik Güncellemeleri için Dosya Takibi
Mevcut kurulumumuzda, sidebar.yaml dosyasını veya Markdown içeriğini değiştirdiğinizde, değişikliklerin web sitesinde görünmesi için FastAPI uygulamasını yeniden başlatmanız gerekir (eğer uvicorn --reload kullanmıyorsanız). Üretim ortamında bu pratik değildir. watchdog gibi bir kütüphane kullanarak docs/ klasöründeki veya sidebar.yaml dosyasındaki değişiklikleri dinleyebilir ve bir değişiklik algılandığında sidebar_config'i otomatik olarak yeniden yükleyebiliriz. Bu, uygulamanın çalışmaya devam ederken içeriğin canlı olarak güncellenmesini sağlar.
# Örnek: main.py içinde bir background task veya event listener olarak
import asyncio
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class ConfigChangeHandler(FileSystemEventHandler):
def __init__(self, app_instance, config_path):
super().__init__()
self.app = app_instance
self.config_path = config_path
def on_modified(self, event):
if event.src_path == str(self.config_path):
print(f"[{event.src_path}] güncellendi, sidebar yapılandırması yeniden yükleniyor...")
# Bu işlem senkron olduğu için dikkatli olun, büyük dosyalarda bloklama yapabilir.
# Alternatif olarak, güncellemeyi bir Queue'ye atıp başka bir process'te işleyebilirsiniz.
self.app.state.sidebar_config = load_sidebar_config(self.config_path) # FastAPI uygulaması içindeki state'i güncelleyin
# Uygulama başlangıcında observer'ı başlatma (FastAPI lifecycle events ile)
# @app.on_event("startup")
# async def startup_event():
# event_handler = ConfigChangeHandler(app, BASE_DIR / "sidebar.yaml")
# observer = Observer()
# observer.schedule(event_handler, path=str(BASE_DIR), recursive=True)
# observer.start()
# app.state.observer = observer # Kapanışta durdurmak için sakla
# @app.on_event("shutdown")
# def shutdown_event():
# if hasattr(app.state, 'observer'):
# app.state.observer.stop()
# app.state.observer.join()
Bu yaklaşım, içerik yönetimini daha dinamik hale getirir ve geliştirici deneyimini önemli ölçüde iyileştirir.
2. Caching (Önbellekleme) Mekanizmaları
Her istekte Markdown dosyasını okumak ve HTML'e çevirmek, özellikle yüksek trafikli sitelerde performans sorunlarına yol açabilir. Bu sorunu çözmek için önbellekleme kullanabiliriz:
- Lru Cache: Python'ın
functools.lru_cachedekoratörü, fonksiyon çağrılarının sonuçlarını önbelleğe almak için basit ama etkili bir yoldur. Markdown'dan HTML'e çeviren fonksiyona uygulayabiliriz. - Redis/Memcached: Daha büyük ölçekli veya dağıtık sistemler için, Redis veya Memcached gibi harici önbellekleme sistemleri kullanılabilir. FastAPI içinde bir
Cachebağımlılığı oluşturarak, sayfa içeriklerini veya hatta tüm render edilmiş HTML parçalarını önbelleğe alabilirsiniz.
# functools.lru_cache kullanımı
from functools import lru_cache
@lru_cache(maxsize=128) # Son 128 farklı dosyanın sonucunu önbelleğe al
def convert_markdown_to_html(md_path: Path) -> str:
# ... mevcut kod ...
Bu basit ekleme, Markdown dönüştürme performansını belirgin şekilde artıracaktır.
3. Arama Fonksiyonu Entegrasyonu
Dokümantasyon sitelerinin vazgeçilmezi olan arama işlevi, kullanıcıların bilgiye hızlıca ulaşmasını sağlar. FastAPI üzerinde bir arama motoru entegre edebilirsiniz:
- Basit Metin Araması: Tüm Markdown dosyalarını tarayarak ve arama terimini içeren dosyaları listeleyerek basit bir arama yapabilirsiniz. Bu, küçük dokümantasyon siteleri için yeterli olabilir.
- Tam Metin Arama Motorları: Elasticsearch, Algolia veya Whoosh (Python tabanlı) gibi daha gelişmiş tam metin arama motorlarını entegre etmek, daha hızlı, daha alakalı ve esnek arama sonuçları sunar. İçerik güncellendiğinde arama indeksini de güncellemek önemlidir.
# Arama endpoint'i örneği
# @app.get("/search", response_class=HTMLResponse)
# async def search_docs(request: Request, query: str):
# results = []
# # Gerçek dünyada burada bir arama motoru (Elasticsearch, Whoosh vb.) entegrasyonu olur.
# # Basit bir örnek olarak, sadece dosya isimlerinde arama yapalım.
# for item in sidebar_config:
# if item.get('type') == 'doc' and query.lower() in item['label'].lower():
# results.append(item)
# elif item.get('type') == 'category':
# for sub_item in item.get('items', []):
# if sub_item.get('type') == 'doc' and query.lower() in sub_item['label'].lower():
# results.append(sub_item)
#
# return templates.TemplateResponse(
# "search_results.html", # Arama sonuçları için ayrı bir şablon
# {"request": request, "sidebar_config": sidebar_config, "title": f"Arama Sonuçları: {query}", "query": query, "results": results}
# )
4. Çok Dilli Destek
Uluslararası projeler için dokümantasyonun birden çok dilde sunulması gerekebilir. Bunu uygulamak için:
- Her dil için ayrı
docs/klasörleri (örn.docs/en/,docs/tr/) ve ayrısidebar.yamldosyaları tutulabilir. - Kullanıcının dil tercihini (tarayıcı ayarlarından veya URL parametresinden) algılayarak dinamik olarak doğru dil setini yükleyebilirsiniz.
Bu, kullanıcıların kendi ana dillerinde dokümantasyona erişmesini sağlayarak genel kullanıcı deneyimini artırır.
5. Özelleştirilebilir Temalar
Docusaurus gibi sistemlerde tema desteği yaygındır. Bizim kurulumumuzda, templates/ ve static/ klasörlerini farklı temaları destekleyecek şekilde genişletebiliriz. Kullanıcılar URL parametresi veya bir ayarla tema seçimi yapabilir ve FastAPI bu seçime göre farklı şablon ve CSS dosyalarını yükleyebilir. Bu, projenizin görsel kimliğini esnek bir şekilde yönetmenizi sağlar.
Bu ileri düzey optimizasyonlar, FastAPI ile kurduğunuz dokümantasyon sitesinin sadece işlevsel değil, aynı zamanda performanslı, ölçeklenebilir ve çeşitli kullanıcı ihtiyaçlarına yanıt verebilir olmasını sağlayacaktır. Her bir adım, projenizin olgunluk seviyesini artırırken, geliştirici ve kullanıcı deneyimini de iyileştirmeye odaklanmıştır.
Sonuç ve Sıkça Sorulan Sorular
Bu makalede, FastAPI ve Jinja2'nin gücünü kullanarak, Docusaurus benzeri dinamik ve esnek bir dokümantasyon sitesi sidebar'ı nasıl oluşturulacağını adım adım öğrendik. sidebar.yaml gibi yapılandırma dosyaları aracılığıyla menü öğelerini tanımlamanın, Markdown içeriğini HTML'e çevirmenin ve tüm bu bileşenleri FastAPI uygulamasında bir araya getirmenin inceliklerini keşfettik. Ortaya çıkan çözüm, statik site üreticilerinin sunduğu kolaylığı dinamik bir web uygulamasının esnekliğiyle birleştirerek, projenizin dokümantasyonunu yönetmek için güçlü bir temel sunmaktadır. Bu yaklaşım, sadece içerik oluşturmaya odaklanmanızı sağlamakla kalmaz, aynı zamanda bakım kolaylığı, tutarlılık ve ölçeklenebilirlik gibi önemli avantajları da beraberinde getirir.
Uygulamamız artık Markdown dokümanlarını otomatik olarak HTML'e dönüştürebiliyor, yapılandırılabilir bir menü ile gezinme sağlıyor ve basit CSS/JS entegrasyonuyla modern bir görünüm sunuyor. Ayrıca, mobil uyumlu tasarım ipuçları ve ileri seviye optimizasyon önerileriyle (önbellekleme, dosya takibi, arama entegrasyonu), projenizi daha da geliştirebileceğiniz yolları gösterdik. FastAPI'nin sunduğu performans ve Jinja2'nin şablonlama yetenekleriyle birleşen bu mimari, kendi özel dokümantasyon çözümünüzü inşa etmek isteyen herkese ilham verecektir.
Sıkça Sorulan Sorular (SSS)
-
S: Neden Docusaurus gibi hazır bir aracı kullanmak yerine kendi çözümümüzü FastAPI ile inşa etmeliyiz?
C: Docusaurus harika bir araç olsa da, FastAPI ile kendi çözümünüzü inşa etmek size daha fazla esneklik ve kontrol sağlar. Özellikle dokümantasyonunuz bir FastAPI web uygulamasıyla sıkı entegrasyon gerektiriyorsa, API'lerden dinamik içerik çekmeniz gerekiyorsa, özel kimlik doğrulama veya yetkilendirme mekanizmaları uygulayacaksanız, veya sadece Python ekosistemi içinde kalmayı tercih ediyorsanız, bu yaklaşım daha uygun olabilir. -
S: Sidebar'da çok sayıda doküman olursa performans sorunları yaşar mıyım?
C: Mevcut haliyle,sidebar.yamldosyasının büyük olması veya çok sayıda Markdown dosyasının her istekte okunup işlenmesi potansiyel bir performans darboğazı yaratabilir. Bu nedenle, makalede bahsedilenlru_cachegibi önbellekleme tekniklerini veya daha büyük projeler için Redis gibi harici bir önbellekleme çözümünü kullanmanız şiddetle tavsiye edilir. -
S: Markdown dosyalarına ek olarak başka formatlarda (örn. reStructuredText, AsciiDoc) doküman ekleyebilir miyim?
C: Evet,convert_markdown_to_htmlfonksiyonu gibi kendi özel dönüştürücü fonksiyonlarınızı yazarak ve FastAPI rotanızda bu fonksiyonları çağırarak farklı doküman formatlarını destekleyebilirsiniz. Örneğin,python-docutilskütüphanesini kullanarak reStructuredText dosyalarını HTML'e çevirebilirsiniz.sidebar.yaml'dekitypealanını genişleterek farklı formatları belirtebilirsiniz. -
S: Sidebar'a dinamik olarak kategori veya doküman eklemenin bir yolu var mı?
C: Evet. Mevcut kurulum,sidebar.yamldosyasını değiştirmenizi gerektirir. Ancak, ileri seviye bir yaklaşım olarak, bir yönetim paneli veya API endpoint'i aracılığıyla veritabanında saklanan kategori/doküman bilgilerini güncelleyebilir ve FastAPI uygulamanızın bu veritabanından sidebar'ı oluşturmasını sağlayabilirsiniz. Ayrıca,watchdogile dosya değişikliklerini izleyerek otomatik güncelleme de mümkündür. -
S: Doküman sayfalarında resimler veya diğer statik varlıklar nasıl kullanılır?
C: Resimlerinizi ve diğer statik varlıklarınızıstatic/klasörünün uygun alt dizinlerine (örn.static/images/) yerleştirebilirsiniz. Markdown dosyalarınızda bu varlıklara referans verirken, FastAPI'nin statik dosya sunumu için ayarladığı/static/URL ön ekini kullanmalısınız. Örneğin:. Bu, tarayıcının doğru dosyayı bulmasını sağlar.
