Client setup

In this section, you learn how to set up your fullstack serverless application with Fauna.

Creating your app

To create a new fullstack serverless app, run the following command in your terminal.

$ npm init next-app fauna-shop --use-npm

Run your app to ensure everything is working correctly before making any changes.

$ cd fauna-shop
$ npm run dev

Navigate to http://localhost:3000 in your browser and review the running application.

Welcome to Next.js

Installing dependencies

Run the following command to add the [Apollo GraphQL client][apollo-client] and GraphQL dependencies to your application.

$ npm install @apollo/client graphql

Configuring your GraphQL client

Create a new file called apollo-client.js in the project’s root with the following code.

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  // Uncomment the appropriate line according to the
  // region group where you created your database.
  uri: '',
  // uri: '',
  // uri: '',

const authLink = setContext((_, { headers }) => {
  const token = process.env.NEXT_PUBLIC_FAUNA_SECRET
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      authorization: token ? `Bearer ${token}` : ''

const client = new ApolloClient({
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),

export default client;

Region groups give you control over where your data resides. You choose the Region Group for your application when you create your database in the first chapter. To learn more about Region Groups visit the documentation.

Authenticating in the front-end

In the previous code snippet, there is an environment variable called NEXT_PUBLIC_FAUNA_SECRET. You ship your client application with a Fauna secret that allows limited access to Fauna resources. A suggested practice is to limit this key to registering and authenticating users. Authenticated users then receive temporary access tokens when they login that grant access to additional resources.

Creating a front-end role

First, create a role that only has permissions to call the RegisterUser and LoginUser functions you created in the User authentication section of the first chapter.

Navigate to the Security section of the Fauna dashboard and choose Roles then New Custom Role.

Creating a new role
  1. Name your role FrontEndRole.
  2. In the Functions section, add the RegisterUser and LoginUser UDFs to this role.
  3. Ensure that Call permissions are selected for both functions.
  4. Choose Save to create the role.
Adding function call permissions to role

Creating roles for your UDFs


When you invoke a UDF that does not have its own role, the UDF runs with the same permissions as the identity that invoked it. In the first section, you invoke your UDFs from the Shell and GraphQL sections of the dashboard. When you are in the dashboard, commands you invoke are run with admin permissions by default.

The key that you create for your front-end application only has permission to invoke the RegisterUser and LoginUser UDFs, but the UDFs need permission to create and read documents in the Owner collection. You do not want to give these permissions directly to the front-end role. Instead, create tightly-scoped roles for each UDF.

Return to the Security section of the dashboard and create another new role.

  1. Name this role RegisterUserUDF.
  2. Select the Owner collection.
  3. Ensure Create permissions are provided, and choose Save to create the new role.
Permissions for the RegisterUserUDF role

Navigate to the Functions section, select the RegisterUser UDF, and update the role to use the new RegisterUserUDF role.

Updating the role for the RegisterUser UDF

Navigate to the Shell and test your UDF by using the Run As feature to invoke the UDF as FrontEndRole.

Invoking a UDF using the "Run As" feature

To use the Run As feature, copy the following query, paste it into the web shell, select FrontEndRole from the Run As menu, and choose Run Query As to invoke your UDF.

  // ["email", "password", "name"]
  ["", "qZXUEhaNdng9", "Improved Security"]
  ref: Ref(Collection("Owner"), "317182757128110665"),
  ts: 1638747899080000,
  data: {
    email: "",
    name: "Improved Security"


Create another new role named LoginUserUDF. The LoginUser UDF needs permission to read from the findOwnerByEmail index to locate the correct user and to read from the Owner collection to compare the hashed credentials for that user.

Updating the role for the LoginUser UDF

Navigate to the Functions section and update the role of your LoginUser UDF to use the new LoginUserUDF role. Return to the Shell and test the UDF, again using Run As to invoke the UDF as the FrontEnd role.

  // ["email", "password"]
  ["", "qZXUEhaNdng9"]
  secret: "<token>",
  ttl: Time("2021-12-06T00:56:46.768682Z"),
  email: ""

Creating a front-end key

Now that your roles and UDFs are working correctly, create a front-end key to store in your application. Navigate to the Security section and choose New Key.

  1. Confirm that your database is selected in the Database field.
  2. Select FrontEndRole in the role selection field.
  3. Name your key.
  4. Choose Save to create the new key.
Creating a front-end key

Copy the secret key to use in the next step.

The secret key cannot be displayed once you navigate away from this page! If you lose a key, you can create a new key with the same role and revoke the old key.

Storing the key in your client

Create a .env.local file in the root of your application and add this secret key as an environment variable.


Restart your Next.js application after updating the environment variable.

Adding an ApolloProvider for Fauna

Adding an ApolloProvider for Fauna allows you to execute GraphQL queries and mutations from your components. To add an ApolloProvider, replace the contents of pages/_app.js with the following code.

import { ApolloProvider } from '@apollo/client';
import client from '../apollo-client';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return (
      <ApolloProvider client={client}>
        <Component {...pageProps} />

export default MyApp;
  • _app.js (0 kb)
  • Replace the contents of pages/index.js with the following code. Make sure to use a valid username and password for a registered user. If you haven’t registered any users yet refer back to the Authentication section for instructions on signing up a new user.

    import styles from '../styles/Home.module.css'
    import { useMutation, gql } from "@apollo/client";
    const LOGIN = gql`
      mutation OwnerLogin($email: String!, $password: String! ) {
        login(email: $email, password: $password) {
    export default function Home() {
      const [loginFunc, { data, loading, error }] = useMutation(LOGIN)
      if (loading) {
        return <div>Loading...</div>;
      if (error) {
        return null;
      const doLogin = e => {
            variables: {
              email: '',
              password: 'qZXUEhaNdng9',
        .then(resp => console.log('==>', resp))
        .catch(e => console.log(e))   
      return (
        <div className={styles.container}>
          <button onClick={doLogin}>Login</button>
  • index.js (0 kb)
  • Run your application with the following command.

    $ npm run dev

    Navigate to localhost:3000 and open your browser’s developer tools to the Console tab. Choose the Login button. You should see the response from the login mutation in the console!

    Login response


    In this session, you configured Fauna to perform user authentication with least privileged access and connected your front-end application to Fauna.

    In the next section, you implement client-side user registration and login forms for your application.

    Complete Code

    📙 Get the final code for this section here