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
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
.env # default variables (all modes)
.env.production # variables only for production mode
.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?