Skip to main content

A jinja2 view template helping function for FastAPI.

Project description

fastapi-view

A lightweight, flexible Jinja2 template rendering library for FastAPI applications, with built-in support for Inertia.js and Vite integration.

Python Version FastAPI License

Features

  • Simple View Rendering: Minimal boilerplate for rendering Jinja2 templates in FastAPI routes
  • Inertia.js Support: FastAPI Inertia.js adapter for building modern monolithic applications
  • Vite Integration: Seamless Vite asset management with HMR support in development
  • Flexible Configuration: Environment-based settings using Pydantic

Installation

pip install fastapi-view

Or using uv:

uv add fastapi-view

Quick Start

Basic View Rendering

from fastapi import FastAPI
from fastapi_view import ViewDepends

app = FastAPI()

@app.get("/")
def index(view: ViewDepends):
    return view.render("index", {"message": "Hello World"})

Set the templates directory via environment variable:

export FV_TEMPLATES_PATH=templates

Inertia.js Integration

from fastapi import FastAPI
from fastapi_view.inertia import InertiaDepends

app = FastAPI()

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    return inertia.render("Dashboard/Index", props={
        "user": {"name": "John Doe", "email": "john@example.com"}
    })

Configure Inertia via environment variables:

export FV_INERTIA_ROOT_TEMPLATE=app.html
export FV_INERTIA_ASSETS_VERSION=v1

Vite Asset Management

In your Jinja2 template:

<!DOCTYPE html>
<html>
  <head>
    {{ vite_hmr_client() | safe }}
    {{ vite_asset('resources/js/app.ts') | safe }}
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Configure Vite settings:

export FV_VITE_DEV_MODE=true
export FV_VITE_DEV_SERVER_URL=http://localhost:5173
export FV_VITE_MANIFEST_PATH=public/build/manifest.json

Configuration

fastapi-view uses Pydantic settings for configuration. All settings can be configured via environment variables with the FV_ prefix.

View Settings

Environment Variable Description Required Default
FV_TEMPLATES_PATH Path to Jinja2 templates directory Yes -

Inertia Settings

Environment Variable Description Required Default
FV_INERTIA_ROOT_TEMPLATE Root template for Inertia responses No app.html
FV_INERTIA_ASSETS_VERSION Asset versioning for cache busting No None

Vite Settings

Environment Variable Description Required Default
FV_VITE_DEV_MODE Enable development mode with HMR No False
FV_VITE_DEV_SERVER_PROTOCOL Protocol for Vite dev server (http/https) No http
FV_VITE_DEV_SERVER_HOST Host for Vite dev server No localhost
FV_VITE_DEV_SERVER_PORT Port for Vite dev server No 5173
FV_VITE_WS_CLIENT_PATH WebSocket client path for HMR No @vite/client
FV_VITE_MANIFEST_PATH Path to Vite manifest file No dist/.vite/manifest.json
FV_VITE_DIST_PATH Path to build output directory No dist
FV_VITE_DIST_URI_PREFIX URI prefix for serving static assets Yes (in production mode) None
FV_VITE_STATIC_URL Base URL for static assets (alternative to uri prefix) Yes (in production mode) None

Note: In production mode (FV_VITE_DEV_MODE=False), either FV_VITE_STATIC_URL or FV_VITE_DIST_URI_PREFIX must be configured.

Advanced Usage

Shared Inertia Props

Share data within a request by calling share() on the Inertia instance:

from fastapi_view.inertia import InertiaDepends

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    # Share props for this request
    inertia.share("app_name", "My Application")
    inertia.share("user", get_current_user())

    return inertia.render("Dashboard/Index", props={"stats": get_stats()})

For sharing data across all routes, use a dependency or middleware:

from fastapi import Depends

def with_shared_props(inertia: InertiaDepends):
    inertia.share("app_name", "My Application")
    inertia.share("version", "1.0.0")

    return inertia

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends = Depends(with_shared_props)):
    return inertia.render("Dashboard/Index", props={"stats": get_stats()})

Optional Props

Use optional props for lazy loading data:

@app.get("/users")
def users(inertia: InertiaDepends):
    return inertia.render("Users/Index", props={
        "users": lambda: fetch_users(),  # Always evaluated
        "metrics": inertia.optional(lambda: fetch_metrics())  # Only on partial reload
    })

Flash Messages

Flash messages for one-time notifications (requires SessionMiddleware):

from starlette.middleware.sessions import SessionMiddleware

app.add_middleware(SessionMiddleware, secret_key="your-secret-key")

@app.post("/login")
def login(inertia: InertiaDepends):
    # ... authentication logic ...
    inertia.flash("success", "Login successful!")
    return inertia.render("Dashboard")

Flash messages are available on the frontend under the flash prop:

<script setup>
import { usePage } from '@inertiajs/vue3'

const page = usePage()
const flash = page.props.flash

// Access flash messages
if (flash.success) {
  console.log(flash.success)
}
if (flash.error) {
  console.log(flash.error)
}
</script>

Deferred Props

Use deferred props to load expensive data after the initial page render, improving perceived performance:

@app.get("/dashboard")
def dashboard(inertia: InertiaDepends):
    return inertia.render("Dashboard/Index", props={
        # Fast data loaded immediately
        "quick_stats": get_quick_stats(),

        # Slow data deferred - loads after initial page render
        "statistics": inertia.defer(lambda: get_statistics(), group="analytics"),
        "recent_activities": inertia.defer(lambda: fetch_activities(), group="analytics"),

        # Another deferred group for separate loading
        "chart_data": inertia.defer(lambda: get_chart_data(), group="charts")
    })

On the frontend, deferred props will be undefined initially, then populated once loaded:

<template>
  <!-- Show skeleton while loading -->
  <div v-if="statistics">
    <p>Total Users: {{ statistics.total_users }}</p>
  </div>
  <div v-else class="animate-pulse">
    <div class="h-4 bg-gray-300 rounded"></div>
  </div>
</template>

<script setup>
defineProps(['statistics'])  // Will be undefined, then populated
</script>

Benefits:

  • Faster initial page load
  • Better user experience with progressive data loading
  • Group related deferred props for efficient batching

Merge Props

Use merge props to combine new data with existing client-side data during partial reloads (perfect for infinite scroll, "load more" features):

@app.get("/posts")
def posts(inertia: InertiaDepends, page: int = 1):
    per_page = 10
    offset = (page - 1) * per_page
    posts = fetch_posts(offset, per_page)

    return inertia.render("Posts/Index", props={
        "posts": inertia.merge(lambda: posts),
        "pagination": {
            "current_page": page,
            "has_more": has_more_posts(page, per_page)
        }
    })

On the frontend, use Inertia's partial reload to append new data:

<template>
  <div>
    <div v-for="post in posts" :key="post.id">
      <h3>{{ post.title }}</h3>
    </div>

    <button v-if="pagination.has_more" @click="loadMore">
      Load More
    </button>
  </div>
</template>

<script setup>
import { router } from '@inertiajs/vue3'

const props = defineProps(['posts', 'pagination'])

function loadMore() {
  router.reload({
    data: { page: props.pagination.current_page + 1 },
    only: ['posts', 'pagination']
  })
}
</script>

Advanced merge options:

# Deep merge for nested objects
inertia.merge(lambda: data).deep_merge()

# Prepend instead of append
inertia.merge(lambda: posts).prepend()

# Append/prepend at specific paths
inertia.merge(lambda: data).append(paths=["items", "results"])

# Prevent duplicates by matching on a field
inertia.merge(lambda: posts).append(match_on="id")

Benefits:

  • Efficient "load more" / infinite scroll implementations
  • Reduced data transfer on partial reloads
  • Automatic client-side data merging

Custom Response Configuration

@app.get("/custom")
def custom_view(view: ViewDepends):
    return view.render(
        "custom",
        context={"data": "value"},
        status_code=201,
        headers={"X-Custom-Header": "value"}
    )

Complete Example

Check out the full Inertia.js example application in the examples/inertia directory, which demonstrates:

  • User authentication flow
  • CRUD operations with Inertia
  • Vite + Vue 3 + TypeScript frontend
  • Session management
  • Form handling

To run the example:

cd examples/inertia
cp .env.example .env
uv sync
bun install
bun run build
uv run uvicorn app.main:app --reload

Development

Setup

git clone https://github.com/turisesonia/fastapi-view.git
cd fastapi-view
uv sync

Running Tests

uv run pytest

Code Formatting

uvx ruff format
uvx ruff check --fix

Requirements

  • Python 3.10+
  • FastAPI 0.70+
  • Jinja2 3.0+
  • Pydantic Settings 2.0+

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Links

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_view-0.6.3.tar.gz (209.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_view-0.6.3-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_view-0.6.3.tar.gz.

File metadata

  • Download URL: fastapi_view-0.6.3.tar.gz
  • Upload date:
  • Size: 209.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_view-0.6.3.tar.gz
Algorithm Hash digest
SHA256 61d8fcdf56df9f3a380bb9e3668d4e366d2e45f0e6d2b5f6e196d09d248a0190
MD5 993e43a1ff14a8860c43489be6fcb290
BLAKE2b-256 2531d83585668ed9b3223187af8cd105d60d185995239591a16d5fe2725352cf

See more details on using hashes here.

File details

Details for the file fastapi_view-0.6.3-py3-none-any.whl.

File metadata

  • Download URL: fastapi_view-0.6.3-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapi_view-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7c4599152a46fd443717548f5a7693dadf17a839e84b2d9a5a3a672968309c05
MD5 8d892356dc5557f7a2119f77716b0cce
BLAKE2b-256 bfa916c50a0f8a6caf7ff005c21b39b58f55678c3d6f2be361cbf31f253cd275

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page