GraphQL

This page demonstrates the steps to creating a GraphQL demo using Apollo. For a full example scope, see learnbit-vue.graphql

To make the whole GraphQL demo works, we need to create the following components:

  1. A custom Vue env for Apollo particularly
  2. An entity component for your data
  3. A pair of Apollo client and Apollo server
  4. A reusable Vue composable to consume data via the Apollo client
  5. A Vue app to eventually render the data

If you want to go straightforwardly to see the final result in advance, run the following to generate a workspace with the components in this tutorial:

$bit
Copiedcopy

Prepare a custom Vue env

Run the following to create a workspace which includes a custom Vue env:

$bit
Copiedcopy

Here, you only need to modify the jest config in the Vue env to ignore transforming the @apollo scope:

// config/jest.config.js

const vueJestConfig = require('@teambit/vue.dev-services.tester.vue-jest-config');
const {
  generateNodeModulesPattern,
} = require('@teambit/dependencies.modules.packages-excluder');

// Override the Jest config to ignore transpiling from specific folders
const packagesToExclude = ['@teambit', '@apollo'];

const transformIgnorePatterns = [
  generateNodeModulesPattern({
    packages: packagesToExclude,
    excludeComponents: true
  }),
]

module.exports = {
  ...vueJestConfig,
  transformIgnorePatterns,
};
CopiedCopy

The other Vue components we will create will be based on this env.

See an example env.

Create an entity component

Create an entity component to define the structure of data being fetched, and provide the methods required for the manipulation of that data. This component serves a a source-of-truth for any component handling that type of data, client-side and server-side.

You can create a new entity component using the following command:

$bit
Copiedcopy

Implement the entity:

export type User = {
  firstName: string;
  lastName: string;
}

export function getFullname(user: User): string {
  return `${user.firstName} ${user.lastName}`;
}
CopiedCopy

Most of the time, the type definition and necessary util functions would be enough.

It also would be great if you create some mocked data in user.mock.ts for further usage:

import { User } from "./user";

export const mockedUser: User = {
  firstName: 'Barry',
  lastName: 'Allen',
};
CopiedCopy

See example component

Another common way to implement an entity is to use an ES class. In this way, you can couple the entity with its methods. However, this is not our focus in this guide.

Create an Apollo client and an Apollo server

Create an Apollo client and an Apollo server to handle the GraphQL requests and responses. The client and server are two separate components, but they are usually created together.

However, if the Apollo server is provided by an existing one or a third party, you can skip the server part and only create the client part.

Create an Apollo server

We create a Node app to serve as the Apollo server. You can create a new Node app using the following command:

$bit
Copiedcopy

Implement the server:

// my-apollo-server.ts

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

const typeDefs = `
type User {
  firstName: String
  lastName: String
}

type Query {
  user: User
}

schema {
  query: Query
}
`;

export const resolvers = {
  Query: {
    user () {
      return {
        firstName: 'Barry',
        lastName: 'Allen',
      }
    },
  },
};

export const createApolloServer = async (port = 4000) => {
  const server = new ApolloServer({
    typeDefs,
    resolvers,
  });

  const { url } = await startStandaloneServer(server, {
    listen: { port },
  });

  console.log(`🚀  Server ready at: ${url}`);
};
CopiedCopy

This app will serve a simple user data in GraphQL.

Then give the app a name my-apollo-server and a way to get port number, which is 4000 by default.

// my-apollo-server.node-app.ts

import { NodeAppOptions } from '@teambit/node';

export const myApolloServer: NodeAppOptions = {
  name: 'my-apollo-server',
  entry: require.resolve('./my-apollo-server.app-root'),
  portRange: [3000, 4000]
};

export default myApolloServer;
CopiedCopy
// my-apollo-server.app-root.ts

import { createApolloServer } from './my-apollo-server';

const port = parseInt(process.argv[2] || '4000', 10);

createApolloServer(port);
CopiedCopy

To run it, you can use the following command:

$bit
Copiedcopy

See example component

Create an Apollo client

We create another component to serve as the Apollo client. It would be reusable in all places in your client side. You can create a new component using the following command:

$bit
Copiedcopy

Implement the client:

import fetch from 'cross-fetch'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'

// HTTP connection to the API
const httpLink = createHttpLink({
  // You should use an absolute URL here
  uri: 'http://localhost:4000/graphql',
  fetch,
})

// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
export const apolloClient = new ApolloClient({
  link: httpLink,
  cache,
})
CopiedCopy

To be notice that the port number should be the same as the one you use in the server.

See example component

Create a Vue composable

We create a Vue composable to consume GraphQL data using the following command:

$bit
Copiedcopy

Implement the composable:

import { computed } from 'vue';
import gql from 'graphql-tag';
import { provideApolloClient, useQuery } from "@vue/apollo-composable";
import { apolloClient } from '@learnbit-vue/graphql.clients.my-apollo-client'
import { User, getFullname } from '@learnbit-vue/graphql.entities.user'

const GET_USERS = gql`
  query user {
    user {
      firstName
      lastName
    }
  }
`

const emptyUser: User = {
  firstName: '',
  lastName: '',
}

export const useUser = () => {
  const query = provideApolloClient(apolloClient)(() => useQuery(GET_USERS))

  const username = computed(() => getFullname(query.result.value?.user || emptyUser))

  return username
}
CopiedCopy

To be notice that it uses:

  • @vue/apollo-composable: the library to integrate Apollo into Vue
  • @learnbit-vue/graphql.clients.my-apollo-client: the Apollo client that we integrate
  • @learnbit-vue/graphql.entities.user: the data entity that we consume

To test your composable, you can use the Apollo server in real world, however, it's highly recommend to mock the Apollo server locally, with the data you mocked previously. In this demo, we do it via the library called msw:

import { graphql } from 'msw'
import { setupServer } from 'msw/node'
import { mockedUser } from '@learnbit-vue/graphql.entities.user'

// import your composable here

const server = setupServer(
  graphql.query('user', (_, res, ctx) => {
    return res(
      ctx.data({
        user: mockedUser
      }),
    )
  })
)

test('basic case', async () => {
  server.listen()

  // write the test code here

  server.close()
})
CopiedCopy

See example component

Create a Vue app

We create a Vue app to render the data using the following command:

$bit
Copiedcopy

Implement the app:

<script setup lang="ts">
import { useUser } from '@learnbit-vue/graphql.composables.user';

const username = useUser();
</script>

<template>
  <div id="app">
    <h1>Hello</h1>
    <p>
      {{ username }}
    </p>
  </div>
</template>
CopiedCopy

To be notice that it uses the composable to get user information and render it on the page.

See example component

Run the whole GraphQL demo

To run the whole GraphQL demo, you can use the following command:

$bit
Copiedcopy
$bit
Copiedcopy

Then you can open the browser to see the result.

To get the whole example locally, you can use this starter:

$bit
Copiedcopy

Conclusion

Now you already know how to create a GraphQL demo using Apollo.

Once again, we recommend:

  1. create entities for your data structure
  2. prepare a GraphQL server necessarily
  3. mock data locally for testing your composables

You can use the same approach to create your own GraphQL demo.