Vue & Nuxt
Build and query a ZXCV database from a Nuxt app, then ship it to the edge. You’ll
end with a page that reads live data with useAsyncData. A short note at the end
covers plain Vue + Vite.
The anon key is public and safe to ship, because access is enforced server-side by your authorization rules.
Create a project
In the dashboard click Start building, or run zxcv
from your terminal. Open Settings → API and copy your project ref and
anon key.
Create a table
In the SQL editor, create a todos table with a couple of rows:
create table todos (
id bigint generated always as identity primary key,
title text not null,
is_complete boolean not null default false
);
insert into todos (title) values ('Read the docs'), ('Ship an app');Install the SDK
npm
bash npm install @zxcv.build/sdkInitialize the client
Add your keys to .env. Nuxt exposes runtimeConfig values to the browser only
when they live under public, and it auto-maps NUXT_PUBLIC_ env vars onto them:
NUXT_PUBLIC_ZXCV_PROJECT=acme-store
NUXT_PUBLIC_ZXCV_ANON_KEY=your-anon-keyDeclare the matching runtimeConfig.public keys:
export default defineNuxtConfig({
runtimeConfig: {
public: {
zxcvProject: '',
zxcvAnonKey: '',
},
},
})Expose a single shared client with a Nuxt plugin, so every component and load
uses the same instance:
import { createClient } from '@zxcv.build/sdk'
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const zxcv = createClient({
project: config.public.zxcvProject,
anonKey: config.public.zxcvAnonKey,
})
return { provide: { zxcv } }
})Fetch and render
Fetch with useAsyncData so the query runs on the server for the first render
and hydrates on the client:
<script setup lang="ts">
const { $zxcv } = useNuxtApp()
const { data: todos } = await useAsyncData('todos', async () => {
const { data, error } = await $zxcv
.from('todos')
.select('id, title, is_complete')
.order('id')
if (error) throw error
return data
})
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
</ul>
</template>Want live updates? Subscribe with Realtime inside
onMounted (client only) — it’s the same client, and the feed is filtered by
the same authorization rules as select.
Deploy
Ship to the global edge from your terminal:
npm i -g zxcv
zxcv link acme-store
zxcv deploy --prodEvery push gets a preview URL; --prod promotes to your domain. See
Deploy for custom domains and canaries.
Plain Vue + Vite
Without Nuxt there’s no server, so fetch from the browser. Vite exposes variables
prefixed with VITE_:
VITE_ZXCV_PROJECT=acme-store
VITE_ZXCV_ANON_KEY=your-anon-keyimport { createClient } from '@zxcv.build/sdk'
export const zxcv = createClient({
project: import.meta.env.VITE_ZXCV_PROJECT,
anonKey: import.meta.env.VITE_ZXCV_ANON_KEY,
})<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { zxcv } from './zxcv'
type Todo = { id: number; title: string; is_complete: boolean }
const todos = ref<Todo[]>([])
onMounted(async () => {
const { data, error } = await zxcv
.from('todos')
.select('id, title, is_complete')
.order('id')
if (error) console.error(error)
else todos.value = data ?? []
})
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
</ul>
</template>Next steps
- Auth — add sign-in and row-level security
- Realtime — live updates in the browser
- SDK reference · Database