Skip to content

Apollo Client

This guide shows you how to setup and use @apollo/client with slicknode-apollo-link.

Setup

First we need to install a few dependencies:

npm install @apollo/client slicknode-apollo-link graphql --save

Creating the Client

To create an instance of the client in your code, you need the endpoint of your Slicknode project. You can get the Slicknode endpoint by running slicknode endpoint in the root folder of your project.

Then create the client in your frontend application and replace the endpoint:

import {
  ApolloClient,
  InMemoryCache,
  HttpLink,
  ApolloLink,
} from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import SlicknodeLink from 'slicknode-apollo-link';

const slicknodeLink = new SlicknodeLink({
  debug: true, // Writes auth debug info to console, disable in production
});

// Log any GraphQL errors or network error that occurred
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.map(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );
  if (networkError) console.log(`[Network error]: ${networkError}`);
});

const SLICKNODE_ENDPOINT = 'https://<yout-slicknode-endpoint>';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  // Create link chain
  link: ApolloLink.from([
    // Add Slicknode link before HttpLink to add auth headers
    slicknodeLink,

    // ...More links for error handling etc...
    errorLink,

    // Network link to make HTTP requests to the API
    new HttpLink({
      uri: SLICKNODE_ENDPOINT,
      credentials: 'same-origin',
      headers: {
        // Uncomment to enable preview mode:
        // 'X-Slicknode-Preview': '1',
        // Uncomment to set default locale:
        // 'X-Slicknode-Locale': 'en-US',
      },
    }),
  ]),
});

That's it! Now we can start fetching data.

Sending Queries

To query data with the ApolloClient, we use the gql template tag that converts plain GraphQL strings into GraphQL query objects.

import { gql } from '@apollo/client';

// ...

client
  .query({
    query: gql`
      {
        viewer {
          user {
            email
          }
        }
      }
    `,
  })
  .then((result) => console.log(result))
  .catch((err) => console.error(err.message));

When you open up your console and execute the code, you should see a log output with a data property that looks something like this:

{
  "data": {
    "viewer": {
      "user": null
    }
  }
}

The viewer field always returns information about the current user that is accessing the data. We have not done any authentication yet, therefore the user field is null.

Authentication

To authenticate a user, the authentication module has to be installed in our project, depending on the type of authenticatio that you want to use. This adds the login mutation to our GraphQL API that we can then query with our client.

Setup

Let's assume we want to use email password authentication. First, we have to make sure that the module with the mutation is available on our server.

To install the module, add it to your backend project via the Slicknode CLI:

slicknode module add auth-email-password

Then deploy the changes to the cloud:

slicknode deploy

Authenticate

To authenticate a user, execute the mutation via the client instance like any other query. You only have to add the directive @authenticate to the query, which tells the SlicknodeLink to use the returned tokens for authentication handling.

In your application where you use the client, you can authenticate a user like this:

client
  .query({
    query: gql`
      mutation LoginUser($email: String!, $password: String!) {
        loginEmailPassword(input: { email: $email, password: $password })
          @authenticate {
          accessToken
          refreshToken
          accessTokenLifetime
          refreshTokenLifetime
        }
      }
    `,
    variables: {
      email: 'email@example.com',
      password: 'mysecretpassword',
    },
  })
  .then((result) => {
    // Check if mutation succeeded
    if (result.data && result.data.loginEmailPassword) {
      console.log('Login successful');
    } else {
      console.log('Login failed, mutation response:', result);
    }
  })
  .catch((err) => {
    console.log('Something went wrong: ', err.message);
  });

Sending Requests

After successful authentication the authentication headers are automatically added to the requests by the SlicknodeLink and all queries are executed with the current user:

// ...

client
  .query({
    query: gql`
      {
        viewer {
          user {
            email
          }
        }
      }
    `,
  })
  .then((result) => console.log(result))
  .catch((err) => console.error(err.message));

This query now returns the current user's email address:

{
  "data": {
    "viewer": {
      "user": {
        "email": "email@example.com"
      }
    }
  }
}

You can also check the debug output of the SlicknodeLink in the console to get information about the authentication process.

Logout

When you want to log the user back out, you can do so by running the logoutUser mutation. You should pass the current refreshToken to the logout mutation to invalidate the token:

client
  .query({
    query: gql`
      mutation LogoutUser($token: String) {
        logoutUser(input: { refreshToken: $token }) {
          success
        }
      }
    `,
    variables: {
      // Get the current refreshToken from the SlicknodeLink instance to invalidate it on the server
      token: slicknodeLink.getRefreshToken(),
    },
  })
  .then((result) => {
    console.log('Logout successful', result.data && result.data.logoutUser);
  })
  .catch((err) => {
    console.log('Something went wrong: ', err.message);
  });

UI Bindings

You can use the client directly to load and update data or use one of the available UI bindings that are available for the major frontend frameworks: