The Ultimate Dev Workflow: From Local FastAPI & Vue Dev to Production Deployment

The Ultimate Dev Workflow: From Local FastAPI & Vue Dev to Production Deployment

The Ultimate Dev Workflow: From Local FastAPI & Due Dev to Production Deployment

In modern web development, the journey from a line of code on your local machine to a live feature serving users is fraught with potential pitfalls. Differences between development and production environments can lead to bugs that only appear after deployment. This guide outlines a robust, three-stage workflow for FastAPI and Vue.js projects that ensures consistency, reliability, and developer productivity, taking you from rapid local development to confident production deployment.

This workflow allows you to:

  • Stage 1: Develop Rapidly with hot-reloading for both your frontend and backend.
  • Stage 2: Test Reliably in a production-like Docker environment on your local machine.
  • Stage 3: Deploy Confidently with a setup that has already been validated.

Let’s build it step by step.


Step 1: Establish a Unified Project Structure

A clean, logical structure is the foundation of a manageable project. This structure is designed to hold all the configuration files for our three-stage workflow.

/your-project
├── .github/
│   └── workflows/
│       └── deploy.yml
├── backend/
│   ├── app/
│   │   └── main.py
│   ├── requirements.txt
│   └── Dockerfile
├── frontend/
│   ├── public/
│   ├── src/
│   ├── package.json
│   ├── pnpm-lock.yaml
│   └── vite.config.js
├── nginx/
│   ├── nginx.conf         # Production Nginx config
│   └── Dockerfile.nginx   # Multi-stage Dockerfile for Vue + Nginx
├── .env.dev               # For Stage 1: Local native development
├── .env.test              # For Stage 2: Local Docker testing
├── .env.prod              # For Stage 3: Production deployment (on server)
├── .gitignore
├── docker-compose.yml     # Base services definition for Docker
└── docker-compose.override.yml # Development-specific Docker settings

Step 2: Master Environment Variables with .env Files

Managing secrets and environment-specific settings is critical. We’ll use a separate .env file for each stage.

Crucial First Action: Add .env* to your .gitignore file immediately. Never commit secrets or environment configurations to your Git repository.

Environment Files Explained

.env.dev (Local Development): For running services directly on your machine.

# For FastAPI running on host
DATABASE_URL=postgresql://user:password@localhost:5433/myapp_dev SECRET_KEY=a_secret_for_local_dev 
# For Vue running on host, points to local backend 
VITE_API_BASE_URL=http://localhost:8000

(We use port 5433 for the database to avoid conflicts with the Docker setup.)

.env.test (Local Docker Testing): For orchestrating containers on your machine.

 # For Docker services 
POSTGRES_DB=myapp_test 
POSTGRES_USER=user 
POSTGRES_PASSWORD=password_for_docker_test 
SECRET_KEY=a_secret_for_docker_test

.env.prod (Production): This file should only exist on your production server.

# For Production deployment 
POSTGRES_DB=myapp_prod 
POSTGRES_USER=prod_user 
POSTGRES_PASSWORD=a_very_strong_production_password SECRET_KEY=a_unique_and_strong_production_secret_key

Stage 1: Fast Local Development Loop

This is your day-to-day mode, optimized for speed and instant feedback with hot-reloading.

How to Run the Backend (FastAPI + Uvicorn)

Setup Python Environment: Navigate to your backend directory and create a virtual environment.

cd backend 
python -m venv venv 
source venv/bin/activate 
pip install -r requirements.txt

Run the Uvicorn Server: From the backend directory, start the server, telling it where to find the development environment file.

 uvicorn app.main:app --reload --env-file ../.env.dev

Your FastAPI backend is now live at http://localhost:8000 and will automatically restart when you save a file.

How to Run the Frontend (Vue + Vite)

Install Dependencies: Navigate to the frontend directory and use pnpm to install packages.

cd frontend
pnpm install

Run the Vite Dev Server:

There is couple of changes you need to make to the frontend configuration. First of all Vite server in development looks for the .env.development file. To make it use .env.dev you need to change the package.json file as follows:

// frontend/package.json

{
  "name": "my-vue-app",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    // Change this line
    "dev": "vite",
    
    // To this
    "dev": "vite --mode dev",

    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    // ...
  }
}

And in the proposed setup the environment files live in the root of the whole project while Vite will look for the files in the frontend directory. To make it look in the root you need to change vite.config.mjs as follows:

// frontend/vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  // This is the line you need to add
  envDir: '../',

  plugins: [vue()],
  resolve: {
    alias: {
      // You might have aliases here, they are unaffected
      '@': '/src',
    }
  }
})

Then run:

pnpm dev

Vite’s server will start (usually on http://localhost:5173), serving your Vue app with hot-module replacement. It will automatically connect to your local backend thanks to the VITE_API_BASE_URL variable.


Stage 2: Integrated Testing with Docker

After developing features, this stage lets you test the entire application stack as a cohesive unit, running exactly as it will in production.

How to Configure the Docker Images

backend/Dockerfile: A standard Dockerfile to containerize the FastAPI application.

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY1 ./app /app/app
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "app.main:app", "-b", "0.0.0.0:8000"]

nginx/Dockerfile.nginx: A powerful multi-stage Dockerfile that first builds the Vue app and then copies the static assets into a lean Nginx image.

# Stage 1: Build Vue.js frontend 
FROM node:18-alpine AS builder 
WORKDIR /app 
COPY frontend/package.json frontend/pnpm-lock.yaml ./ 
RUN npm install -g pnpm && pnpm install 
COPY frontend/ ./ 
RUN pnpm build 

# Stage 2: Setup Nginx production server 
FROM nginx:1.27-alpine 
COPY --from=builder /app/dist /usr/share/nginx/html 
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf 
EXPOSE 80 
CMD ["nginx", "-g", "daemon off;"]

How to Configure Docker Compose

We use two files: a base file defining the production-like services, and an override file that adds development conveniences like volume mounting for live code updates inside containers.

docker-compose.yml (Base Configuration):

version: '3.8' 

services: 
  nginx: 
    build: 
      context: . 
      dockerfile: nginx/Dockerfile.nginx 
    ports: 
      - "80:80" 
    depends_on: 
      - backend 

  backend: 
    build: ./backend 
    env_file: .env.test 
    environment: 
      -DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} 
    depends_on: 
      db: 
        condition: service_healthy 

  db: 
    image: postgres:15-alpine 
    env_file: .env.test 
    ports: 
      - "5433:5432" # Map to host 5433 to avoid local conflicts 
    volumes: 
      - postgres_data:/var/lib/postgresql/data 
    healthcheck: 
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] 
      interval: 10s 
      timeout: 5s 
      retries: 5 

volumes: 
  postgres_data:

docker-compose.override.yml (Development Overrides):

version: '3.8' 
services: 
  backend: 
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload 
    volumes: 
      - ./backend/app:/app/app # Mount local code for hot-reloading 
  nginx: 
    ports: - "8080:80" # Use a different host port for testing

How to Run the Test Environment

Ensure the .env.test file exists in your project root.

From the root directory, launch the entire stack:

 docker-compose up --build

Docker Compose automatically merges the base and override files. You can now access your full, integrated application at http://localhost:8080, confident it’s running in a consistent, production-like environment.


Stage 3: Confident Production Deployment

Because you’ve tested in a near-identical Docker environment, deploying is no longer a leap of faith. It’s a simple, repeatable process.

How to Prepare for Production Deployment

On your production server, your CI/CD pipeline (e.g., GitHub Actions) will automate these steps. You will need a production-ready compose file.

  • docker-compose.prod.yml: This file is explicit and secure. It might add configurations for HTTPS certificates and production logging. YAMLversion: '3.8' services: nginx: ports: - "80:80" - "443:443" # Expose HTTPS port volumes: - /etc/letsencrypt:/etc/letsencrypt:ro # Mount SSL certificates backend: command: gunicorn -w 4 -k uvicorn.workers.UvicornWorker app.main:app -b 0.0.0.0:8000 # No code volumes in production! The code is baked into the image. # The db service can often be identical to the base docker-compose.yml

How to Deploy

Your CI/CD pipeline script on the production server will execute a set of simple commands:

Bash

# 1. Log in to your Docker registry (if using one)

# 2. Pull the latest code changes from your main branch
git pull

# 3. Bring down the old version and launch the new one using the prod config
# The -f flag explicitly specifies which compose files to use.
# The -d flag runs the containers in detached mode.
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

# 4. Clean up old, unused Docker images to save disk space
docker image prune -af

By following this multi-stage workflow, you create a seamless and reliable bridge from development to production, empowering you to build and deploy features faster and with greater confidence.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *