DEV Community

Cover image for Integrating Vite with Flask for Production
Dan Hackworth
Dan Hackworth

Posted on

Integrating Vite with Flask for Production

In this post, we'll explore how to configure Vite and Flask to run seamlessly together in a production environment. This setup is ideal for developers looking to leverage the speed of Vite for frontend development with the power and simplicity of Flask for the backend.

Prerequisites

Before we dive into the configurations, let's assume you have the basic project structure set up. This includes a standard Flask application and a Vite-powered React frontend.

Configuring Vite for Development

We start by setting up our vite.config.js file for the development environment. The configuration ensures that Vite serves our React frontend and proxies API requests to our Flask backend.

Here's the vite.config.js file for development:


import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
  base: './',
  plugins: [react()],
  build: {
    assetsDir: 'static',
  },
  server: {
    port: 3000,
    cors: true,
    proxy: {
      "/api": {
        target: "http://127.0.0.1:5555/",
        changeOrigin: true,
        secure: false,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • The base is set to './' to ensure that the built paths are relative.
  • assetsDir is configured as 'static', aligning with Flask's static file serving convention.
  • The development server runs on port 3000.
  • CORS is enabled for development.
  • A proxy is set up for requests starting with /api, redirecting them to the Flask server running on http://127.0.0.1:5555/. ##Adapting Configuration for Production For production, we simplify the configuration by removing the development server settings. Since Flask will serve our static files in production, we don't need Vite's development server and its proxy settings.

In the production version of vite.config.js, simply omit the server object:

// ... rest of the configuration
build: {
  assetsDir: 'static',
},
// No server object for production
Enter fullscreen mode Exit fullscreen mode

In the upcoming sections, we'll dive deeper into how to configure Flask to serve the Vite-built assets and ensure that both Flask and Vite work harmoniously in a production setting.

Backend Configuration in Flask

Moving to the backend, let's talk about the Flask configuration. While many developers prefer to place the Flask app initialization in app.py, I choose to place it in config.py. This keeps all configurations centralized, making the codebase cleaner and easier to maintain.

Furthermore, for production, special attention is given to how the static_folder and template_folder are set up, especially since we're integrating with a Vite frontend. Here's what the config.py file looks like in production:


import os
from dotenv import load_dotenv

load_dotenv()
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_migrate import Migrate
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from flask_cors import CORS

app = Flask(
  __name__, 
  static_folder='../client/dist/static',
  template_folder='../client/dist'
)

app.secret_key = os.getenv('SECRET_KEY')
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('SUPABASE_URI')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config
['SESSION_COOKIE_NAME'] = 'id'
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.json.compact = False

metadata = MetaData(naming_convention={
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
})
db = SQLAlchemy(metadata=metadata)
migrate = Migrate(app, db)
db.init_app(app)
bcrypt = Bcrypt(app)
api = Api(app)
CORS(app)
Enter fullscreen mode Exit fullscreen mode

For the production setup in app.py, it's essential to add a catch-all route to handle redirects for single-page applications:


@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    return render_template("index.html")

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

This setup ensures that all routes are correctly redirected to the index.html file, a common requirement for single-page applications in production.

Building and Starting the App
When deploying to a platform like Render or similar, ensure you use Gunicorn for the backend. Assuming your project is structured into two separate folders (client for the frontend and server for the backend) and your Pipenv dependencies are transferred into a requirements.txt file, (which can be done via command pip freeze > requirements.txt) your build command would look like this:

pip install -r requirements.txt && npm install --prefix client && npm run build --prefix client
Enter fullscreen mode Exit fullscreen mode

And the start command for Gunicorn would be:

gunicorn --chdir server app:app
Enter fullscreen mode Exit fullscreen mode

Overcoming Limitations: Serving Images from the Public Folder

A limitation I encountered with this setup involved serving images from the public folder. Vite handles this well in development, but Flask doesn't automatically serve these files in production. To overcome this, I utilized Supabase's storage feature, which provided a more scalable and professional solution.

The Solution:

Supabase Storage and Server-Side Rendering
Images are uploaded to Supabase, offering a scalable and efficient solution. The Flask backend fetches image URLs server-side, and the frontend, served by Vite, displays the images. This method enhanced scalability and performance.

Implementation Overview

  1. Uploading Images to Supabase Storage: Secure and structured storage solution.

2.Fetching Image URLs Server-Side: Flask backend dynamically retrieves image URLs.

3.Rendering Images in the Frontend: Vite-served frontend displays images using these URLs.

Conclusion

This challenge led to discovering a robust solution. Using Supabase Storage for image handling not only addressed the initial issue but also added significant benefits in scalability and performance. This approach, combining Flask's server-side capabilities with Supabase's storage, resulted in a more professional and scalable application.

Combining Vite and Flask for a production environment offers an efficient and modern web development experience. By addressing and overcoming specific challenges, the integration of these two powerful tools can lead to the creation of high-performance, scalable web applications.

Top comments (0)