Best practice to switch between development and production urls when using Vue/Vite and Django

I want to build a website with Vue/Vite as frontend and Django as a backend.
When my frontend needs to get data from the backend I currently do something like this (which works perfectly fine):
const response = await fetch('http://localhost:8000/api/register', {...

But for a production environment I (probably?) want something like this to be able to switch between dev and prod without manually changing every url:
const response = await fetch(backend_url + '/register', {...

My Question is, what is the best/standard way to setup this backend_url in Vue/Vite?

Kind regards
feps

My Search so far:
Vue has a provide/inject feature which could be used to define a gloable Variables in my App.vue file.
Vite has "Shared options" defined in vite.config.js
But for both options I was unable to find any example, which leaves me wondering if there is another option which I was unable to find, which is used to solve this "problem".

The best practice would be to implement the following:

  1. Abstract your VueJS calls to the backend in a utility file that you import in your components. This will allow for a "single point of control" scenario where the backend URL is only defined in one spot

    Example:

    /util/api.js

    /*
     * Required Types
     *
     * url: String
     * method: String
     * headers: Object
     * body:
     * query: [{'key': 'value'}, {'key': 'value'}... ]
     * onFailure: Function
     */
    export const httpRequest = async (params) => {
      let { url_endpoint, method, headers, body, query, onFailure } = params;
      let url = ""
    
      // utilize Vite's environment variables to detect mode
      if (import.meta.env.MODE === "development") {
        url = "http://localhost:8000" + url_endpoint;
        requestCredentials = "include";
      } else if (import.meta.env.MODE === "production"){
        // TODO: Define production URL
      }
      // append query params to url
      if (Array.isArray(query) && query.length) {
        url += "?";
        query.forEach(
          (q) => (url += `${Object.keys(q)[0]}=${Object.values(q)[0]}&`),
        );
      }
    
      try {
        return await fetch(url, {
          method: method,
          credentials: requestCredentials,
          headers: headers,
          body: body,
        });
      } catch(e) {
        console.error(e)
        return onFailure()
      }
    
    
    
  2. Import your httpRequest helper method in your VueJS file and interact with your backend as desired.

    /views/HomeView.vue

    <script setup>
    import { httpRequest } from "@/util/api.js";
    const res = await httpRequest({
      url_endpoint: "/api/register",
      method: "GET",
      onFailure: () => console.error("Could not fetch register"),
    });
    </script>
    
    
  3. Bonus: Configure alias paths in vite.config.js

    import { fileURLToPath, URL } from "node:url";
    
    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue";
    
    // https://vitejs.dev/config/
    export default defineConfig({
      server: {},
      plugins: [vue()],
      resolve: {
        alias: {
          "@": fileURLToPath(new URL(".", import.meta.url)),
        },
      },
    });
    
    

Usually there is no need to switch base urls at runtime, and at build time this is handled with Vite's env vars. There will be VITE_API_BASE_URL variable that can depend on build mode and local environment.

Use high-level wrappers for fetch API like Ky or Axios or your own wrapper function to provide base url to all requests once instead of concatenating VITE_API_BASE_URL everywhere.

tl;dr step by step version

  1. create a file in the root of your app called .env and put this in the file
    VITE_API_URL="'http://localhost:8000/"
    
  2. in your vue app file, add this to the top
    API_URL = import.meta.env.VITE_API_URL
    
  3. when you need to use it in the same file, you can just do
    const response = await fetch(`${API_URL}api/register`, {...
    

that should work, and on the production server, you will have a different .env file with a different value for this.
Don't forget to add .env to your .gitignore so you don't commit it to your app.

You might also want to consider having a file called api.js/ts in your app where you are storing your api calls so things becomes easier for you in the long run.

here's some references for you

Vite automatically loads environment variables from files named like this:

File Use when
.env Loaded in all modes
.env.[mode] Loaded only when you run for Mode

I use the following for my projects

  1. .env # default variables (all modes)

  2. .env.production # variables only for production mode

  3. .env.staging # variables only for staging mode

.env.production

VITE_API_URL=http://api.website.com

VITE_APP_NAME=MyAppProd

.env.staging

VITE_API_URL=http://localhost:8000

VITE_APP_NAME=MyAppStaging

.env

VITE_API_URL=http://localhost:8000

VITE_APP_NAME=MyAppStaging

Use in your app file.

const base_api = import.meta.env.VITE_API_URL

In package.json:

"scripts": {
    "dev": "vite --mode staging",
    "build": "vue-tsc && vite build --mode production",
    ........
 },

but you don't checkout your env files in git, so how do you do the build during ci/cd?

Вернуться на верх