Through my experience building several production applications with FastAPI and Vue, I've developed a template that combines the best of both worlds: FastAPI's powerful backend capabilities with Vue 3's reactive frontend features. This guide shares the approach that has successfully served applications handling thousands of daily users.
Personal Experience Note: When I first combined FastAPI and Vue, coordinating the development and build processes was challenging. After several production deployments, I've refined a setup that provides a smooth development experience while maintaining optimal production performance.
Project Structure
Here's the project structure I've found most effective for FastAPI + Vue applications:
fastapi-vue-project/ ├── backend/ │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py │ │ ├── core/ │ │ │ ├── config.py │ │ │ └── security.py │ │ ├── api/ │ │ │ ├── v1/ │ │ │ └── deps.py │ │ ├── models/ │ │ ├── schemas/ │ │ └── services/ │ ├── requirements.txt │ └── alembic/ ├── frontend/ │ ├── src/ │ │ ├── assets/ │ │ ├── components/ │ │ ├── composables/ │ │ ├── stores/ │ │ ├── views/ │ │ ├── router/ │ │ ├── types/ │ │ ├── App.vue │ │ └── main.ts │ ├── public/ │ ├── index.html │ ├── package.json │ ├── tsconfig.json │ └── vite.config.ts ├── docker/ │ ├── backend/ │ └── frontend/ ├── nginx/ │ └── conf.d/ ├── .env ├── docker-compose.yml └── README.md
Backend Setup
First, let's set up the FastAPI backend with CORS support for Vue:
# backend/app/main.py from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles app = FastAPI(title="FastAPI + Vue App") # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173"], # Vue dev server allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # API routes @app.get("/api/v1/health") async def health_check(): return {"status": "healthy"} # Serve Vue app in production app.mount("/", StaticFiles(directory="../frontend/dist", html=True), name="static")
Frontend Setup
Initialize a Vue 3 project with Vite and TypeScript:
# Create Vue project npm create vite@latest frontend -- --template vue-ts # Install essential dependencies cd frontend npm install vue-router@4 pinia @vueuse/core axios
Configure Vite for development and production:
// frontend/vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, }, }, }, build: { outDir: 'dist', sourcemap: false, }, })
API Integration
Set up a type-safe API client:
// frontend/src/services/api.ts import axios, { AxiosInstance, AxiosError } from 'axios' import { useAuthStore } from '@/stores/auth' export const createAPI = (): AxiosInstance => { const api = axios.create({ baseURL: '/api/v1', headers: { 'Content-Type': 'application/json', }, withCredentials: true, }) api.interceptors.request.use((config) => { const authStore = useAuthStore() if (authStore.token) { config.headers.Authorization = `Bearer ${authStore.token}` } return config }) api.interceptors.response.use( (response) => response, async (error: AxiosError) => { if (error.response?.status === 401) { const authStore = useAuthStore() authStore.logout() } return Promise.reject(error) } ) return api }
State Management
Implement a Pinia store for authentication:
// frontend/src/stores/auth.ts import { defineStore } from 'pinia' import { ref } from 'vue' import type { User } from '@/types' import { api } from '@/services/api' export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem('token')) const user = ref (null) async function login(credentials: { email: string; password: string }) { const response = await api.post('/auth/login', credentials) token.value = response.data.token user.value = response.data.user localStorage.setItem('token', token.value) } function logout() { token.value = null user.value = null localStorage.removeItem('token') } return { token, user, login, logout } })
Vue Components Structure
Example of a well-structured Vue component:
Loading...{{ error }}
{{ key }} {{ item[key] }}
Development Environment
Configure Docker Compose for development:
# docker-compose.yml version: '3.8' services: backend: build: context: ./backend dockerfile: ../docker/backend/Dockerfile volumes: - ./backend:/app ports: - "8000:8000" environment: - DATABASE_URL=postgresql://user:password@db:5432/dbname depends_on: - db frontend: build: context: ./frontend dockerfile: ../docker/frontend/Dockerfile volumes: - ./frontend:/app - /app/node_modules ports: - "5173:5173" environment: - VITE_API_URL=http://localhost:8000 db: image: postgres:13 environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=password - POSTGRES_DB=dbname volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data:
Production Deployment
Configure Nginx as reverse proxy:
# nginx/conf.d/default.conf server { listen 80; server_name localhost; location /api { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } }
Performance Optimization
Key optimizations for production:
// 1. Frontend optimization in vite.config.ts export default defineConfig({ build: { rollupOptions: { output: { manualChunks: { 'vue-vendor': ['vue', 'vue-router', 'pinia'], 'utils': ['axios', '@vueuse/core'], } } } } }) # 2. Backend caching from fastapi_cache import FastAPICache from fastapi_cache.backends.redis import RedisBackend @app.on_event("startup") async def startup(): redis = aioredis.from_url("redis://localhost") FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache:")
Conclusion
This template provides a solid foundation for building full-stack applications with FastAPI and Vue 3. The combination delivers excellent developer experience while maintaining high performance in production.
Remember: While this template provides a strong starting point, adapt it to your specific needs. Keep frontend and backend concerns separate, maintain type safety, and optimize for your deployment environment.
For more information, refer to the official documentation for FastAPI and Vue.js.