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:
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() }Import your
httpRequesthelper 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>Bonus: Configure alias paths in
vite.config.jsimport { 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
- create a file in the root of your app called
.envand put this in the fileVITE_API_URL="'http://localhost:8000/" - in your vue app file, add this to the top
API_URL = import.meta.env.VITE_API_URL - 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