Vue Integration
Mearie provides Vue composables that integrate seamlessly with Vue's reactive system for automatic query refetching and full type safety.
Installation
Install the core package and the Vue integration:
npm install -D mearie
npm install @mearie/vueyarn add -D mearie
yarn add @mearie/vuepnpm add -D mearie
pnpm add @mearie/vuebun add -D mearie
bun add @mearie/vuedeno add --dev npm:mearie
deno add npm:@mearie/vueSetup
1. Add Build Plugin
Add Mearie's build plugin to enable automatic type generation from your GraphQL documents:
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import mearie from 'mearie/vite';
export default defineConfig({
plugins: [vue(), mearie()],
});TIP
By default, Mearie looks for ./schema.graphql relative to your vite.config.ts. For custom schema locations or advanced configuration, see Codegen Config.
2. Create Client
Create a GraphQL client with your API endpoint. Import createClient and exchanges from @mearie/vue:
// src/lib/graphql-client.ts
import { createClient, httpExchange, cacheExchange, dedupExchange } from '@mearie/vue';
import { schema } from '$mearie';
export const client = createClient({
schema,
exchanges: [
dedupExchange(),
cacheExchange(),
httpExchange({
url: 'https://api.example.com/graphql',
}),
],
});See Exchanges for more details on available exchanges and middleware.
3. Set Up Provider
Wrap your app with the client provider to make the GraphQL client available throughout your component tree:
// src/main.ts
import { ClientPlugin } from '@mearie/vue';
import { client } from './lib/graphql-client';
app.use(ClientPlugin, { client });Composables
useQuery
Fetch data with automatic caching:
<script setup lang="ts">
import { graphql } from '$mearie';
import { useQuery } from '@mearie/vue';
const props = defineProps<{ userId: string }>();
const { data, loading, error, refetch } = useQuery(
graphql(`
query GetUserQuery($id: ID!) {
user(id: $id) {
id
name
email
avatar
bio
age
}
}
`),
() => ({ id: props.userId }),
);
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else>
<img :src="data.user.avatar" :alt="data.user.name" />
<h1>{{ data.user.name }}</h1>
<p v-if="data.user.bio">{{ data.user.bio }}</p>
<p>Email: {{ data.user.email }}</p>
<p>Age: {{ data.user.age }}</p>
<button @click="refetch()">Refresh</button>
</div>
</template>previousData is a ref holding data from a previous set of variables — never an earlier value of the current ones. While a new set of variables loads, data is undefined but previousData still holds that earlier result, so computed(() => data.value ?? previousData.value) keeps the current view rendered instead of falling back to a loading state. See Data Ownership for the full contract.
useMutation
Modify data with automatic cache updates:
<script setup lang="ts">
import { ref } from 'vue';
import { graphql } from '$mearie';
import { useMutation } from '@mearie/vue';
const props = defineProps<{ userId: string }>();
const name = ref('');
const [updateUser, { loading }] = useMutation(
graphql(`
mutation UpdateUserMutation($id: ID!, $name: String!) {
updateUser(id: $id, input: { name: $name }) {
id
name
}
}
`),
);
const handleSubmit = async () => {
await updateUser({ id: props.userId, name: name.value });
};
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="name" required />
<button :disabled="loading">{{ loading ? 'Saving...' : 'Save' }}</button>
</form>
</template>useFragment
Co-locate data requirements with components:
<script setup lang="ts">
import { graphql } from '$mearie';
import { useFragment } from '@mearie/vue';
import type { UserCard_user$key } from '$mearie';
const props = defineProps<{ user: UserCard_user$key }>();
const fragment = useFragment(
graphql(`
fragment UserCard_user on User {
id
name
avatar
email
}
`),
() => props.user,
);
</script>
<template>
<div class="card">
<img :src="fragment.data.avatar" :alt="fragment.data.name" />
<h3>{{ fragment.data.name }}</h3>
<p>{{ fragment.data.email }}</p>
</div>
</template>useSubscription
Real-time updates via subscriptions:
<script setup lang="ts">
import { graphql } from '$mearie';
import { useSubscription } from '@mearie/vue';
const props = defineProps<{ chatId: string }>();
const { data, loading } = useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
body
author {
name
}
}
}
`),
() => ({ chatId: props.chatId }),
);
</script>
<template>
<div>
<div>{{ loading ? 'Connecting...' : 'Connected' }}</div>
<div v-if="data?.messageAdded">
<strong>{{ data.messageAdded.author.name }}:</strong>
{{ data.messageAdded.body }}
</div>
</div>
</template>data holds the latest event only, and previousData holds the last event received under a previous set of variables.
Reactive Variables
Variables automatically track Vue reactivity:
<script setup lang="ts">
import { ref, computed } from 'vue';
import { graphql } from '$mearie';
import { useQuery } from '@mearie/vue';
const userId = ref('123');
// Automatically refetches when userId changes
const { data } = useQuery(
graphql(`
query GetUserQuery($id: ID!) {
user(id: $id) {
id
name
email
}
}
`),
() => ({ id: userId.value }),
);
// Or with computed
const variables = computed(() => ({ id: userId.value }));
const { data: data2 } = useQuery(
graphql(`
query GetUserQuery($id: ID!) {
user(id: $id) {
id
name
email
}
}
`),
variables,
);
</script>
<template>
<div v-if="data">
<h1>{{ data.user.name }}</h1>
</div>
</template>Re-execution depends on exactly three things: the observer key (the document and its variables), skip, and the fetchPolicy value. They are the composable's explicit watch sources and each is compared by value, so other reactive values you read inside the variables or options getters are read at execution time and do not re-trigger the query on their own.
A variables change is applied before the next render rather than at the moment you write it, so a synchronous read taken immediately after the write — before the watcher flushes — can still observe the transitional state.
Next Steps
- Queries - Learn more about queries
- Mutations - Learn more about mutations
- Fragments - Learn more about fragments
- Subscriptions - Learn more about subscriptions