Skip to content

Getting Started with GraphQL in Vue 3 — Complete Setup with Apollo

Published: at 

Introduction

GraphQL Comic
GraphQL simplifies data fetching for APIs.

For over a year now, I’ve been working with GraphQL and a Backend-for-Frontend (BFF) at my job. Before this role, I had only worked with REST APIs and Axios, so it’s been a big learning curve. That’s why I want to share everything I’ve learned over the past months with you. I’ll start with a small introduction and continue adding more posts over time.

What is GraphQL and why should Vue developers care?

GraphQL is a query language for APIs.
You send a query describing the data you want, and the server gives you exactly that. Nothing more. Nothing less.

For Vue developers, this means:

GraphQL and the Vue 3 Composition API go together like coffee and morning sun.
Highly reactive. Highly type-safe. Way less code.

Try it yourself

Here is a GraphQL explorer you can use right now. Try this query:

query {
  countries {
    name
    emoji
    capital
  }
}

💡 If the embed breaks, open it in a new tab.

Under the hood: GraphQL is just HTTP

GraphQL Meme
GraphQL is just HTTP.

GraphQL feels magical.
Underneath, it is just an HTTP POST request to a single endpoint like /graphql.

Here is what a query looks like in code:

const COUNTRIES = gql`
  query AllCountries {
    countries {
      code
      name
      emoji
    }
  }
`

Apollo transforms that into a regular POST request:

curl 'https://countries.trevorblades.com/graphql' \
  -H 'content-type: application/json' \
  --data-raw '{
    "operationName": "AllCountries",
    "variables": {},
    "query": "query AllCountries { countries { code name emoji } }"
  }'

Request parts:

The server parses it, runs it, and spits back a JSON shaped exactly like you asked.
That is it. No special protocol. No magic. Just structured HTTP.

GraphQL as your BFF (Backend For Frontend)

Backend Services

GraphQL BFF Layer

useQuery/useMutation

Vue 3 Application

Apollo Client

InMemoryCache

GraphQL Server

Country Service

User Service

Other Services

One of GraphQL’s real superpowers: it makes an amazing Backend For Frontend layer.

When your frontend pulls from multiple services or APIs, GraphQL lets you:

And thanks to caching:

Compared to fetch or axios juggling REST endpoints, GraphQL feels like you just switched from horse-drawn carriage to spaceship.

It gives you:

Where Apollo fits in

Apollo Client is the most popular GraphQL client for a reason.

It gives you:

If you know TanStack Query, the mapping is simple:

TanStack QueryApollo Client
useQueryuseQuery
useMutationuseMutation
QueryClient cacheApollo InMemoryCache
DevtoolsApollo Devtools

Main difference: Apollo speaks GraphQL natively. It understands operations, IDs, and types on a deeper level.

Now let us build something real.

1. Bootstrap a fresh Vue 3 project

npm create vite@latest vue3-graphql-setup -- --template vue-ts
cd vue3-graphql-setup
npm install

2. Install GraphQL and Apollo

npm install graphql graphql-tag @apollo/client @vue/apollo-composable

3. Create an Apollo plugin

Create src/plugins/apollo.ts:

import { import DefaultApolloClientDefaultApolloClient } from "@vue/apollo-composable";
import type { interface App<HostElement = any>App } from "vue";
import { import ApolloClientApolloClient, import createHttpLinkcreateHttpLink, import InMemoryCacheInMemoryCache } from '@apollo/client/core'

const const httpLink: anyhttpLink = import createHttpLinkcreateHttpLink({
  uri: stringuri: 'https://countries.trevorblades.com/graphql'
})

const const cache: anycache = new import InMemoryCacheInMemoryCache()

const const apolloClient: anyapolloClient = new import ApolloClientApolloClient({
  link: anylink: const httpLink: anyhttpLink,
  cache: anycache,
})

export const 
const apolloPlugin: {
    install(app: App): void;
}
apolloPlugin
= {
function install(app: App): voidinstall(app: App<any>app: interface App<HostElement = any>App) { app: App<any>app.App<any>.provide<any, any>(key: any, value: any): App<any>provide(import DefaultApolloClientDefaultApolloClient, const apolloClient: anyapolloClient); }, };

This wraps Apollo cleanly inside a Vue plugin and provides it across the app.

4. Install the plugin

Edit src/main.ts:

import { const createApp: CreateAppFunction<Element>createApp } from 'vue'
import import AppApp from './App.vue'
import { import apolloPluginapolloPlugin } from './plugins/apollo'
import './style.css'

const const app: App<Element>app = function createApp(rootComponent: Component, rootProps?: Data | null): App<Element>createApp(import AppApp)

const app: App<Element>app.App<Element>.use<[]>(plugin: Plugin<[]>): App<Element> (+1 overload)use(import apolloPluginapolloPlugin)

const app: App<Element>app.App<Element>.mount(rootContainer: string | Element, isHydrate?: boolean, namespace?: boolean | ElementNamespace, vnode?: VNode): ComponentPublicInstancemount('#app')

Done. Apollo is now everywhere in your app.

5. Your first GraphQL query

Create src/components/CountryList.vue:

<script setup lang="ts">
import { import useQueryuseQuery } from '@vue/apollo-composable'
import import gqlgql from 'graphql-tag'

const const COUNTRIES: anyCOUNTRIES = import gqlgql`
  query AllCountries {
    countries {
      code
      name
      emoji
    }
  }
`

const { const result: anyresult, const loading: anyloading, const error: anyerror } = import useQueryuseQuery(const COUNTRIES: anyCOUNTRIES)
</script>

<template>
  <section: HTMLAttributes & ReservedPropssection>
    <h1: HTMLAttributes & ReservedPropsh1 HTMLAttributes.class?: anyclass="text-2xl font-bold mb-4">🌎 Countries (GraphQL)</h1: HTMLAttributes & ReservedPropsh1>

    <p: HTMLAttributes & ReservedPropsp v-if="const loading: anyloading">Loading…</p: HTMLAttributes & ReservedPropsp>
    <p: HTMLAttributes & ReservedPropsp v-else-if="const error: anyerror" HTMLAttributes.class?: anyclass="text-red-600">{{ const error: anyerror.message }}</p: HTMLAttributes & ReservedPropsp>

    <ul: HTMLAttributes & ReservedPropsul v-else HTMLAttributes.class?: anyclass="grid gap-1">
      <li: LiHTMLAttributes & ReservedPropsli v-for="const c: anyc in const result: anyresult?.countries" key?: PropertyKey | undefined:key="const c: anyc.code">
        {{ const c: anyc.emoji }} {{ const c: anyc.name }}
      </li: LiHTMLAttributes & ReservedPropsli>
    </ul: HTMLAttributes & ReservedPropsul>
  </section: HTMLAttributes & ReservedPropssection>
</template>

Drop it into App.vue:

<template>
  <import CountryListCountryList />
</template>

<script setup lang="ts">
import import CountryListCountryList from './components/CountryList.vue'
</script>

Fire up your dev server:

npm run dev

You should see a live list of countries.
No REST call nightmares. No complex wiring.

6. Bonus: add stronger types (optional)

Apollo already types generically.
If you want perfect types per query, you can add GraphQL Code Generator.

I will show you how in the next post. For now, enjoy basic type-safety.

7. Recap and what is next

✅ Set up Vue 3 and Vite
✅ Installed Apollo Client and connected it
✅ Ran first GraphQL query and rendered data
✅ Learned about proper GraphQL package imports

👉 Coming next: Type-Safe Queries in Vue 3 with graphql-codegen
We will generate typed useQuery composables and retire manual interfaces for good.

Source Code

Find the full demo here: example

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now

Related Posts