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.
One-time setup
Section titled “One-time setup”1. Describe the stands
Section titled “1. Describe the stands”turnout server add main --url https://api.example.com --label "Main API"turnout server add second --url https://api2.example.orgAdd --insecure for a stand with a self-signed certificate.
2. Save access (optional)
Section titled “2. Save access (optional)”turnout credential add main-login --user myname # who logs inturnout pass set main-login # hidden secret, into the OS keyringLater turnout pass copy main-login puts the password on your clipboard whenever the stand asks for it.
3. Register the app
Section titled “3. Register the app”From the project directory:
cd ~/dev/myshopturnout app addThe 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:
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.
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:
export default defineConfig({ server: { proxy: { "/api": "http://localhost:7100" }, },});with VITE_API_URL=/api. Either way, only localhost appears in the project.
Every day
Section titled “Every day”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 5100Open 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.
Switch stands
Section titled “Switch stands”turnout use myshop secondThat’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.
See the whole picture
Section titled “See the whole picture”$ turnout statusturnout 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 yetThe same in one line for your prompt - second · gw ok - is status --short; turnout use - takes myshop back to main.
What the gateway handles for you
Section titled “What the gateway handles for you”- Stand redirects (
Location: https://api.example.com/...) come back rewritten tohttp://localhost:7100/...- the browser never escapes to the stand. - WebSocket connections are proxied the same way (
wssfor https stands), with the same jar cookies. - A stand that is down answers with a clear
502: stand 'main' is unreachableinstead of a cryptic proxy error - and the gateway says so instatusand 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.
When a second app appears
Section titled “When a second app appears”It gets its own port without being asked (7101, and so on); group the contour and switch everything at once:
turnout group add front --app myshop --app adminturnout use front second