Kodall
Web App Deployment

Vue 3 (Vite)

Step-by-step guide to building, proxying, and deploying standalone Vue 3 Vite applications to a Kodall instance with @kodall/kodall-deploy.

This guide covers building a standalone Vue 3 Single Page Application using Vite, configuring the Vite development proxy, integrating @kodall/kodall-client, and deploying with @kodall/kodall-deploy.

This guide demonstrates a reference integration pattern for Vue 3. Kodall natively hosts any frontend application that compiles to static web assets (HTML, JavaScript, CSS, WebAssembly).

Vue 3 (Vite) Live Demo

Shared Demo Mode

Deployed on Kodall instance at /vue

Want an isolated database for this demo?

Currently running in shared demo mode with the public API key. Create a private 24-hour sandbox with 1-click to test creating, editing, and deleting records without interference.

https://docs-demo.kodall.io/vue

Installation

Install @kodall/kodall-client and @kodall/kodall-deploy in your Vue 3 project:

BASH
pnpm add @kodall/kodall-client
pnpm add -D @kodall/kodall-deploy

Configuration

Configure proxying inside vite.config.ts using the kodallProxy plugin from @kodall/kodall-deploy/vite to automatically sync with kodall-webapp.config.json:

vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { kodallProxy } from '@kodall/kodall-deploy/vite'

export default defineConfig({
  plugins: [
    vue(),
    // Option A: Automatic Vite Plugin (Recommended)
    kodallProxy() // Automatically resolves instance from default_env
  ]

  // Option B: Manual Vite Proxy (Without Helper Plugin)
  // server: {
  //   proxy: {
  //     '/auth': { target: 'https://dev.kodall.yourcompany.com', changeOrigin: true, secure: true },
  //     '/rest': { target: 'https://dev.kodall.yourcompany.com', changeOrigin: true, secure: true },
  //     '/storage': { target: 'https://dev.kodall.yourcompany.com', changeOrigin: true, secure: true }
  //   }
  // }
})
kodallProxy() automatically resolves target URLs, custom proxy_paths, and proxy routes from default_env in your configuration. To target a specific environment, pass { env: 'staging' } or explore the Local Dev Proxy Suite.

Client Setup

Create a Vue composable to provide a shared instance of KodallClient:

src/composables/useKodall.ts
import { KodallClient } from '@kodall/kodall-client'

const client = new KodallClient()

export function useKodall() {
  return client
}
Because the app runs on the same origin in production and is proxied during local development, new KodallClient() requires no hardcoded baseUrl or credentials.

Deployment Configuration (kodall-webapp.config.json)

Vite compiles production builds to the ./dist directory:

kodall-webapp.config.json
{
  "web_app_name": "Vue 3 Dashboard",
  "web_app_path": "/",
  "dist_path": "./dist",
  "default_env": "dev",
  "environments": {
    "dev": {
      "type": "dev",
      "instance": "https://dev.kodall.yourcompany.com",
      "api_key": "dev-api-key-here"
    },
    "prod": {
      "type": "prod",
      "instance": "https://kodall.yourcompany.com",
      "api_key": "prod-api-key-here"
    }
  }
}
Instance Configuration: Do not use https://docs-demo.kodall.io. Enter the base URL of your own Kodall instance deployed from the Kodall Cloud Console.

Usage

Authentication & Session Restore

Support both API Key (stateless bearer token) and Username/Password (cookie session), restoring the session on page refresh without UI flicker:

src/App.vue
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { KodallClient, isProblem } from '@kodall/kodall-client'

const isInitializing = ref(true)
const isAuthenticated = ref(false)
const credentials = reactive({ apiKey: '', username: '', password: '' })

function getClient(customKey?: string): KodallClient {
  return new KodallClient({ apiKey: customKey || credentials.apiKey || undefined })
}

// Connect with API Key
async function handleConnectApiKey(keyInput?: string) {
  const key = keyInput || credentials.apiKey
  if (!key.trim()) return
  const client = getClient(key)
  const result = await client.fetch('FETCH todo (key) LIMIT 1')
  if (!isProblem(result)) {
    isAuthenticated.value = true
    localStorage.setItem('kodall_is_logged_in', 'true')
    localStorage.setItem('kodall_auth_mode', 'apikey')
    localStorage.setItem('kodall_api_key', key)
  }
}

// Login with Username & Password
async function handleLoginBasic(uInput?: string, pInput?: string) {
  const u = uInput || credentials.username
  const p = pInput || credentials.password
  if (!u || !p) return
  const client = getClient('')
  const result = await client.auth({ user: u, password: p }, { roles: true })
  if (!isProblem(result)) {
    isAuthenticated.value = true
    localStorage.setItem('kodall_is_logged_in', 'true')
    localStorage.setItem('kodall_auth_mode', 'basic')
  }
}

// Restore session on mount (anti-flicker loading splash)
onMounted(async () => {
  try {
    const params = new URLSearchParams(window.location.search)
    const urlApiKey = params.get('apiKey')
    if (urlApiKey) credentials.apiKey = urlApiKey

    if (localStorage.getItem('kodall_is_logged_in') === 'true') {
      const mode = localStorage.getItem('kodall_auth_mode')
      if (mode === 'basic') await handleLoginBasic()
      else await handleConnectApiKey()
    }
  } finally {
    isInitializing.value = false
  }
})
</script>

Parent-Child Entity Operations

Fetch hierarchical parent-child relationships in a single FETCH query (e.g. Master todo with joined todo_item child subtasks) and create master-detail records:

TYPESCRIPT
// 1. Fetch joined parent-child rows
const result = await client.fetch<any>(`
  FETCH todo (key, title, is_completed) {
    todo_item TO id_todo (key AS item_key, description, is_completed AS item_completed)
  }
  ORDER BY key DESC
  LIMIT 100
`)

// 2. Group flat rows into structured tree objects
function groupJoinedTodos(rows: any[]) {
  const map = new Map<number, any>()
  for (const row of rows) {
    if (!map.has(row.key)) {
      map.set(row.key, {
        key: row.key,
        title: row.title,
        is_completed: row.is_completed,
        children: { todo_todo_item: [] }
      })
    }
    if (row.item_key != null) {
      map.get(row.key).children.todo_todo_item.push({
        key: row.item_key,
        description: row.description,
        is_completed: row.item_completed
      })
    }
  }
  return Array.from(map.values())
}

// 3. Create parent entity and child items atomically
await client.create({
  entity_name: 'todo',
  properties: {
    title: 'Deploy to Kodall',
    is_completed: 0
  },
  children: {
    todo_todo_item: [
      { entity_name: 'todo_item', properties: { description: 'Configure vite.config.ts', is_completed: 1 } },
      { entity_name: 'todo_item', properties: { description: 'Deploy application', is_completed: 0 } }
    ]
  }
})

Storage API & File Uploads

Upload and download files directly using Kodall's storage endpoints:

TYPESCRIPT
// Upload a file (Blob or File instance from <input type="file">)
const response = await fetch('/storage', {
  method: 'POST',
  headers: {
    'Content-Type': selectedFile.type || 'application/octet-stream',
    'X-File-Name': encodeURIComponent(selectedFile.name)
  },
  body: selectedFile
})

const storageRecord = await response.json()
console.log('Stored File ID:', storageRecord.id)

Deployment

Package Scripts

Add build and deployment scripts to package.json:

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "deploy": "vite build && kodall-deploy -e dev",
    "deploy:prod": "vite build && kodall-deploy -e prod"
  }
}

Executing Deployment

Execute the deployment command for your target environment:

BASH
# Deploy to development environment
pnpm deploy

# Or deploy to production
pnpm run deploy:prod

# Or run interactively
npx kodall-deploy

kodall-deploy will validate ./dist, create a compressed archive, upload it to Kodall storage, update the web_app entity, and execute a live HTTP health check ping.


Advanced Configuration

Subpath Mounting

If your app is deployed to a subpath (e.g. "web_app_path": "/portal"), configure base in vite.config.ts:

vite.config.ts
export default defineConfig({
  base: '/portal/',
  plugins: [vue()]
})

Deployment History & Rollback

Every deployment is tracked in Kodall storage. You can inspect previous versions and instantly roll back to any prior build:

BASH
# Inspect live status across instances
npx kodall-deploy --status

# View deployment history
npx kodall-deploy --history -e prod

# Roll back to the previous deployment
npx kodall-deploy --rollback -e prod

Troubleshooting

Missing index.html in ./dist

  • Cause: The Vite build command was not executed prior to running kodall-deploy.
  • Solution: Ensure your build script runs vite build before calling kodall-deploy.

CORS Errors During Local Development

  • Cause: Requests are targeting the remote backend directly instead of the Vite dev proxy.
  • Solution: Verify that client requests use relative URLs (/auth, /rest, /storage) matching the server.proxy entries in vite.config.ts.