Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History

README.md

Outline

Vite React + FastAPI

Screenshot of the todo list UI for the Vite React + FastAPI sample (light theme) Screenshot of the todo list UI for the Vite React + FastAPI sample (dark theme)

Todo app with React frontend and Python FastAPI backend using YARP for unified routing.

Architecture

Run Mode:

flowchart LR
    Browser --> YARP
    YARP -->|/api/*| FastAPI[FastAPI Backend]
    YARP --> Vite[Vite Dev Server<br/>HMR enabled]
Loading

Publish Mode:

flowchart LR
    Browser --> YARP[YARP serving<br/>Vite build output<br/>'npm run build']
    YARP -->|/api/*| FastAPI[FastAPI Backend]
Loading

What This Demonstrates

  • addUvicornApp: Python FastAPI backend with uv package manager
  • addViteApp: React + TypeScript frontend with Vite
  • addYarp: Single endpoint with path-based routing
  • withTransformPathRemovePrefix: Strip /api prefix before forwarding
  • publishWithStaticFiles: Frontend embedded in YARP for publish mode
  • Dual-Mode Operation: Vite HMR in run mode, Vite build output in publish mode
  • Polyglot Fullstack: JavaScript + Python working together

Running

aspire run

Commands

aspire run      # Run locally
aspire deploy   # Deploy to Docker Compose
aspire do docker-compose-down-dc  # Teardown deployment

Key Aspire Patterns

YARP with Path Transform - Strip /api prefix before forwarding to FastAPI:

const api = await builder.addUvicornApp("api", "./api", "main:app")
    .withHttpHealthCheck({ path: "/health" });

const frontend = await builder.addViteApp("frontend", "./frontend")
    .withReference(api);

await builder.addYarp("app")
    .withConfiguration(async (yarp) =>
    {
        const apiCluster = await yarp.addClusterFromResource(api);
        await (await yarp.addRoute("api/{**catch-all}", apiCluster))
            .withTransformPathRemovePrefix("/api");

        if (await executionContext.isRunMode.get())
        {
            const frontendCluster = await yarp.addClusterFromResource(frontend);
            await yarp.addRoute("{**catch-all}", frontendCluster);
        }
    })
    .publishWithStaticFiles(frontend);

Path Transform Example:

  • Client: GET /api/todos
  • YARP receives: /api/todos
  • Transform strips: /api
  • FastAPI receives: GET /todos

Security Notes

This sample intentionally keeps the FastAPI todo endpoints public and unauthenticated for instructional use. The /api/todos endpoints are demo endpoints, not a production API security model.

  • Todo data is stored in process memory and is lost when the API restarts. The API caps the in-memory list at 100 todos, limits todo titles to 200 characters, requires non-empty titles, and bounds list responses with skip and limit query parameters.
  • The sample does not include authentication, authorization, CSRF protection, or rate limiting.
  • For production, add an appropriate identity provider and authorization checks, persistent storage, CSRF protections if browser credentials are used, rate limiting/quotas, request logging/monitoring, and deployment-specific network protections.

Relevant references:

Morty Proxy This is a proxified and sanitized view of the page, visit original site.