# Debouncing GraphQL Apollo in Vue

## Scenario

You have an input that must make a real-time query to the database. This is often an autocomplete, but in my case it is checking to see if a subdomain is unique.

## Input Component

I am using [PrimeVue](https://primevue.org/), so that is where the components are coming from.

```xml
            <div class="flex flex-col gap-2">
              <label for="subdomain">Organization Subdomain</label>
              <InputText
                id="subdomain"
                v-model="subdomain"
                placeholder="i.e. grace-church-bc"
                :class="{ 'p-invalid': errors.subdomain }"
                fluid
              />
              <Message v-if="errors.subdomain" severity="error" variant="simple" size="small">{{
                errors.subdomain
              }}</Message>
              <Message
                v-if="subdomainMessage.message && !subdomainLoading"
                :severity="subdomainMessage.severity"
                variant="simple"
                size="small"
              >
                {{ subdomainMessage.message }}
              </Message>
            </div>
```

Nice.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750848551866/20236736-9080-4305-8ba9-caea74cbb7fd.png align="center")

## GraphQL File Structure

When I work with [GraphQL](https://graphql.org/), [Apollo](https://www.apollographql.com/), and [Vue Apollo](https://apollo.vuejs.org/) , I find separation extremely important. GraphQL is flexible in its ability to fetch just what you need, but it requires a lot of boiler plate. I find the following setup very good for the frontend portion—

```plaintext
src
|_graphql
    |_operations
        |_index.ts // barrel file
        |_subdomain.ts // one of many operation files
    |_composables
        |_index.ts // barrel file
        |_useCheckSubdomainAvailability.ts // one of many composables
    |_types
        |_index.ts // generated by codegen
|_App.vue
...
```

## Operations

```typescript
import { gql } from "graphql-tag";

export const CHECK_SUBDOMAIN_AVAILABILITY = gql`
  query CheckSubdomainAvailability($subdomain: String!) {
    checkSubdomainAvailability(subdomain: $subdomain) {
      available
      subdomain
    }
  }
`;
```

This is a simple GraphQL client query that uses the `graphql-tag` library to parse the GQL syntax. It takes a string and then calls our resolver `checkSubdomainAvailability` with the string as its argument. We are asking that the resolver give back `available` and `subdomain`. We don’t really need `subdomain`, but I don’t feel like changing the existing code at the moment.

## Composables

```typescript
import { useQuery } from "@vue/apollo-composable";
import { CHECK_SUBDOMAIN_AVAILABILITY } from "../operations/subdomain";
import { computed, type Ref } from "vue";
import { CheckSubdomainAvailabilityQuery } from "../types";

export function useCheckSubdomainAvailability(
  subdomain: Ref<string | undefined, string | undefined>,
) {
  const { result, loading, error, refetch } = useQuery<CheckSubdomainAvailabilityQuery>(
    CHECK_SUBDOMAIN_AVAILABILITY,
    computed(() => ({ subdomain: subdomain.value ?? "" })),
    {
      fetchPolicy: "no-cache",
      debounce: 500,
    },
  );

  const isAvailable = computed(() => result.value?.checkSubdomainAvailability?.available);

  return {
    loading,
    error,
    isAvailable,
    refetch,
  };
}
```

This is where the magic really happens.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Notice that we are passing a <code>Ref</code> and not a <code>String</code>. This is vital as Vue Apollo’s <code>useQuery()</code> composable will <em>refetch automatically when a ref is updated!</em></div>
</div>

We then break out the usual variables from the `useQuery` composable. Notice we add the generated type using a TypeScript Generic, which allows us to have type safety wherever we use this composable throughout the application.

Next, notice that we are able to pass an options object to the `useQuery` composable. We pass `fetchPolicy: "no-cache"` just in case another user creates the same subdomain within the cache duration. This keeps the data fresh. Secondly, we pass `debounce: 500`. This means the composable will only make the query `500ms` after the data changes and also cancel any previous requests it had started.

## The Impact

This is crucial as we would otherwise overload our server with requests on every keystroke.

![not-debounced.mov [video-to-gif output image]](https://s2.ezgif.com/tmp/ezgif-287afe58bbbe05.gif align="left")

But, with debounce, things work great.

![debounced.mov [video-to-gif output image]](https://s2.ezgif.com/tmp/ezgif-2ee44d72e3c58c.gif align="left")

I love that debounce is built into the [Vue Apollo](https://apollo.vuejs.org/) composable so we do not need to create a separate function and utilize a third party library like [VueUse](https://vueuse.org/shared/useDebounceFn/#usedebouncefn) or [Lodash](https://lodash.com/docs/4.17.15#debounce) to accomplish this task.
