Skip to content

Daily Development with a Vite App

This guide walks through the full path for a typical frontend setup: a Vue 3 + Vite app whose API lives on two stands - https://api.example.com and https://api2.example.org. The goal: switching stands becomes one command, and your project’s env files never change again.

Terminal window
turnout server add main --url https://api.example.com --label "Main API"
turnout server add second --url https://api2.example.org

Add --insecure for a stand with a self-signed certificate.

Terminal window
turnout credential add main-login --user myname # who logs in
turnout pass set main-login # hidden secret, into the OS keyring

Later turnout pass copy main-login puts the password on your clipboard whenever the stand asks for it.

From the project directory:

Terminal window
cd ~/dev/myshop
turnout app add

The wizard walks every field of the app. It detects the package manager from the lock file and proposes the standard commands (pnpm dev --port {port}, pnpm build, … - a Vite dev script takes its port from turnout), which you can correct, rename or extend on the spot; then it asks which variable carries the gateway URL to the app (it proposes VITE_API_URL for a Vite project), which dotenv file to keep in step, which dev server port to pin (empty lets the first dev assign one), which directory the build lands in, and which servers the app may use.

You are not asked for a gateway port. turnout takes the first free one from 7100-7199 - say 7100 - and writes it into the dotenv file on the spot. The same, scripted:

Terminal window
turnout app add myshop --path . --env-var VITE_API_URL --server main --server second
# Wrote ./.env.development.local (VITE_API_URL=http://localhost:7100).

4. Point the app at the gateway - once and forever

Section titled “4. Point the app at the gateway - once and forever”

This is the core idea: the project knows only its gateway port, never a stand address - and it does not even write the port down. The port is turnout’s, assigned when the app was registered; the app is handed the address two ways: turnout dev puts VITE_API_URL=http://localhost:7100 into the dev server’s environment, and .env.development.local (written by turnout, ignored by git, read by Vite in development only) covers pnpm dev from an IDE. Pin another port with turnout app edit myshop --port 7200 and both follow.

src/api.ts
import axios from "axios";
export const api = axios.create({ baseURL: import.meta.env.VITE_API_URL });

If your stand is strict about CORS or the Origin header, proxy through Vite instead - requests become same-origin:

vite.config.ts
export default defineConfig({
server: {
proxy: { "/api": "http://localhost:7100" },
},
});

with VITE_API_URL=/api. Either way, only localhost appears in the project.

Terminal window
turnout use myshop main # which stand to work against
# 'myshop' now uses 'main'.
# Stand check: https://api.example.com responded with 200 OK.
turnout gateway start # once per workday
# myshop: http://localhost:7100
# Front door: http://localhost
# myshop: http://myshop.localhost
turnout dev # from anywhere inside the project
# [myshop] pnpm dev --port 5100
# [myshop] http://myshop.localhost -> dev server port 5100

Open myshop.localhost (turnout open myshop does it): the UI is local, on the port turnout gave the dev server - the same one tomorrow - and every API call travels through localhost:7100 to the stand. Log in to the stand - its session cookie never reaches your browser; it lands in the gateway’s jar for the myshop+main pair.

Terminal window
turnout use myshop second

That’s it. No Vite restart, no env edits: the very next request goes to api2.example.org, because the gateway re-reads the binding on every request. Need the password for the other stand? turnout pass copy second - it is on the clipboard.

And when you come back with turnout use myshop main, you are still logged in: the main cookies were waiting in their own jar.

$ turnout status
turnout 0.24.1 C:\Users\me\AppData\Local\lacodda\turnout
Apps
myshop -> second http://myshop.localhost spare :7100 (VITE_API_URL)
Gateway running · pid 18324 · up 25m
front door http://localhost
Catalog
servers main, second
credentials main-login
paths none yet
targets none yet

The same in one line for your prompt - second · gw ok - is status --short; turnout use - takes myshop back to main.

  • Stand redirects (Location: https://api.example.com/...) come back rewritten to http://localhost:7100/... - the browser never escapes to the stand.
  • WebSocket connections are proxied the same way (wss for https stands), with the same jar cookies.
  • A stand that is down answers with a clear 502: stand 'main' is unreachable instead of a cryptic proxy error - and the gateway says so in status and on the desktop, as it does when the stand stops taking your session.
  • If the gateway itself restarts, the only cost is logging in to the stand again - jars live in the process memory.

It gets its own port without being asked (7101, and so on); group the contour and switch everything at once:

Terminal window
turnout group add front --app myshop --app admin
turnout use front second