Making GraphQL Codegen Work For You: GraphQL Integration with React and TypeScript
Published: 2023-03-01 11:09:45

## TL;DR

In this guide, we’ll be showing you how to use GraphQL alongside React and GraphQL Codegen to create a simple page that can pull data from an API and send emails. We’ll be using [Novu as an open source notification system](https://novu.co/) for developers that can send our emails after being passed through a form created within React. 

## Intro

We’ve all been there: you’ve been coding away in React with Typescript enjoying type-checking until it’s time to time to integrate your application with an API. And then, all of your data types are wrong, ending up with a ton of errors, or just not working at all. Thankfully, integrating APIs doesn’t need to be complicated with GraphQL. It enables you to quickly get an API integrated without the hassle of a standard REST API, thanks to tools like Codegen.

In this article, I will demonstrate the problem that many developers face and how to improve the workflow when integrating your react app with a GraphQL API. 

*This tutorial assumes that you have been working with React before and want to improve, so you already have your workstation set up.*

![GraphQL Meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/coswyh37xvdaji4yv0ez.png)


### What We Will Do:

- **Bootstrap** a new Next.js TypeScript project
- **Install** Apollo GraphQL and fetch data from a public API
- **Showcase** why using GraphQL Codegen is a good idea
- **Create** a Next.js API endpoint for sending emails with data
- **Use the [Novu.co](http://Novu.co) platform** for setting up and sending emails to a given email

*If you want to skip the writeup, you can go to GitHub directly and look at the code: [https://github.com/ugglr/next-typescript-graphql-integration](https://github.com/ugglr/next-typescript-graphql-integration)*

## Creating an Example Project with React and Next.js

Learning is faster when you get your hands dirty, so let’s jump straight in. We’ll bootstrap a new React project using Next.js with Typescript. Next.js is a full-stack framework for React that allows us to quickly start up a web app.  

Run the following command in your terminal to scaffold a new Next.js project with Typescript:

```bash
yarn create next-app --typescript
```

It will then prompt you for the name of the project. Let’s name it `next-typescript-graphql-integration` so we know what we’re working with, and we’ll leave anything else to default values. 

While we’re still in the terminal, navigate to our newly created project and run it to make sure that the installation was successful. 

```bash
cd next-typescript-graphql-integration

yarn dev
```

The output will be similar to this:

```bash
yarn dev
yarn run v1.22.15
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
event - compiled client and server successfully in 1457 ms (165 modules)
```

From this, we can determine that the page is available at **localhost:3000**. Opening the browser and checking the page will show the default Next.js screen.


![Screenshot of Next.JS Default Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u4wp2jyxm6g732mq2d93.png)

## Install Dependencies & Initialize Apollo Client

We are now ready to install some packages. We start by installing one of the most popular GraphQL clients, [Apollo Client](https://www.apollographql.com/apollo-client). Apollo is a tool that enables devs to utilize GraphQL APIs within almost any tech stack and integrates it within your UI. Then, we’ll start doing some fetching from [this public Rick & Morty GraphQL API](https://studio.apollographql.com/public/rick-and-morty-a3b90u/home?variant=current).

```bash
yarn add @apollo/client graphql
```

When that’s done, let’s open up our code editor and initialize the Apollo Client. When inspecting the folder you can see that it’s a standard bare-bones Next project with the following folder structure:

```bash
root
--- .next
--- pages
--- public
--- styles
```

Inside of `_app.tsc` we can start initializing our client by following the steps described by the official [Apollo Client React docs](https://www.apollographql.com/docs/react/get-started/).

We include the necessary imports and create the client, then include it in our React app via `ApolloProvider`. In the end, the file should look something like this:

```tsx
import "@/styles/globals.css";
import type { AppProps } from "next/app";

import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://rickandmortyapi.com/graphql",
  cache: new InMemoryCache(),
});

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ApolloProvider {...{ client }}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}
```

This is everything we need to initialize our brand new GraphQL client on the client side. 🚀 Let’s continue with fetching some data **the un-safe way**.

## Fetching Data without Type Checking

Now that we have our project up and running, it’s time to add some data from our API. If we want to get a list of characters from the API we need to run a query like so:

```tsx
query {
  characters {
    results {
      id
      name
			species
    }
  }
}
```

The Apollo documentation will tell us to create the query document and then use it together with the `useQuery` hook provided by Apollo. I’ve deleted everything in `index.tsx` and replaced it with the following:

```tsx
import { gql, useQuery } from "@apollo/client";
import { NextPage } from "next";

const GET_CHARACTERS = gql`
  query {
    characters {
      results {
        id
        name
				species
      }
    }
  }
`;

const Home: NextPage = () => {
  const { loading, data } = useQuery(GET_CHARACTERS);

  return (
    <div>
      <main>
        {loading && <p>Loading...</p>}

        {data?.characters.results.map((character) => (
          <p key={character.id}>{character.name}</p>
        ))}
      </main>
    </div>
  );
};

export default Home;
```

This is what it will look like, so let’s check back to our application:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aiayfvm8nxv4jdfq6u3y.png)

Everything seems like it’s good, right? 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ow8f335zezv9e1uml5m.jpg)

## What Went Wrong with GraphQL?

While our code may work as intended, it’s not exactly reliable. There are two common errors we made here that we need to look at:

### No GraphQL Type Validation

We didn’t add any type validation to our GraphQL calls. This means if you request a field that doesn’t exist in the schema, you will get weird and hard-to-understand error messages. Let’s see what can happen if we change our project:

```tsx
const GET_CHARACTERS = gql`
  query {
    characters {
      results {
        id
        name
        species
				notInSchema // 👈 this will result in error
      }
    }
  }
`;
```

Running this will give us an “Error! Response not successful: Received status code 400”, which doesn’t tell you what went wrong at all, becoming a huge time waster.

Considering that some queries and mutations might have a variety of variables, fields, and subfields this gets complex **fast** (believe me, I’ve done this to my breaking point before!). So you should be ensuring that your GraphQL queries don’t call for items not in the schema.

### No Types In The Response

We also didn’t include any form of autocomplete in our response, so our code doesn’t know what is supposed to be returned from our API. We need to ensure that we validate the data we receive from our API to ensure our data is correct. If we don’t, this can become a nightmare in the future.

Let’s take a look at our code editor. You should see a red line under the `character` variable with the following Typescript validation error:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d3xti92ccmh0jw3pm65o.png)

So how do we fix this issue?

If we’re just building something small, we can create our own type like so:

```tsx
type GetCharactersQueryResponse = {
  characters: {
    results: Array<{
      id: string;
      name: string;
			species: string;
    }>;
  };
};
```

And then passing it into our query hook:

```tsx
const { loading, error, data } =
    useQuery<GetCharactersQueryResponse>(GET_CHARACTERS);
```

This will give us type-checking from Typescript and work just fine. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9ix9r7c5xjgslhrebg1.png)

But this solution becomes a nightmare when return types become large or if you need to pass variables. All of those are checked in the GraphQL API and will send you confusing errors that are hard to debug. 

Fortunately, there are some smart people who realized that all this information is already available inside GraphQL schemas & can be used to generate this information for us.  

Enter: GraphQL Codegen 🚀

## What Is Graphql Codegen?

[Graphql Codegen](https://github.com/dotansimha/graphql-code-generator) is a code generation library for GraphQL that enables developers to generate custom code. It provides us developers with the ability to generate type definitions, query builders, documentation, and more by analyzing our GraphQL schemas. This makes it easier and faster to build GraphQL applications and reduces the time spent coding.

Additionally, Graphql Codegen is also a type-safe code generator. This means that the generated code is checked for errors by the Graphql Codegen compiler **before** it is used in an application. This ensures that any errors in the code are caught before they cause problems in production.

Plus, it also provides developers with an easy way to manage GraphQL schemas. It allows devs to easily add, remove, and update their schemas without any hassle. This makes it much easier to keep your schemas up-to-date.

## Setup and Configuring GraphQL Codegen

Before we can start using Graphql Codegen, we’ll need to configure it. This process is relatively simple and can be done in a few steps:

The first step is installing GraphQL Codegen by running this command 👨‍💻

```bash
yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-graphql-request @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
```

**This will install the following packages as `devDepencencies`** 

- `@graphql-codegen/cli`
- `@graphql-codegen/typescript`
- `@graphql-codegen/typescript-graphql-request`
- `@graphql-codegen/typescript-operations`
- `@graphql-codegen/typescript-react-apollo`

After installing, we need to configure GraphQL Codegen. I prefer to do this by adding a `.yml` file to the root of our directory.

```bash
$ touch graphql.config.yml
```

Open up the file and let’s add the following configuration:

```bash
schema:
  - "https://rickandmortyapi.com/graphql" 
documents:
  - "./graphql/**/*.graphql"
generates:
  ./generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
```

**The configuration file does the following:**

- `schema` This points to our GraphQL endpoint for fetching the API schema map.
- `documents` This tells GraphQL Codegen where to look for our schema files
- `generates` This tells GraphQL Codegen where to create and store generated code.
- `plugins` Specifies what GraphQL Codegen plugins to use.

To finalize the setup we also need to add the generation script to our `package.json`

```json
{
  "name": "next-typescript-graphql-integration",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "generate": "graphql-codegen --config graphql.config.yml" 👈 here
  },
  "dependencies": {
    "@apollo/client": "^3.7.7",
    "@next/font": "13.1.6",
    "@types/node": "18.11.18",
    "@types/react": "18.0.27",
    "@types/react-dom": "18.0.10",
    "eslint": "8.33.0",
    "eslint-config-next": "13.1.6",
    "graphql": "^16.6.0",
    "next": "13.1.6",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "typescript": "4.9.5"
  },
  "devDependencies": {
    "@graphql-codegen/cli": "^3.0.0",
    "@graphql-codegen/typescript": "^3.0.0",
    "@graphql-codegen/typescript-graphql-request": "^4.5.8",
    "@graphql-codegen/typescript-operations": "^3.0.0",
    "@graphql-codegen/typescript-react-apollo": "^3.3.7"
  }
}

```

If you run `yarn generate` now you will see an error output like this:

```bash
yarn generate
yarn run v1.22.15
$ graphql-codegen --config ./graphql.config.yml
(node:15632) ExperimentalWarning: stream/web is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
✔ Parse Configuration
❯ Generate outputs
✔ Parse Configuration
⚠ Generate outputs
  ❯ Generate to ./generated/graphql.ts
    ✔ Load GraphQL schemas
    ✖
      Unable to find any GraphQL type definitions for the f…
      - ./graphql/**/*.graphql
    ◼ Generate
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
```

This means the setup is complete and we are ready to add some schema files.

## Adding Our Schema File

Starting off, create a folder in the root of the directory called `graphql`, and inside create a file called `get-characters.query.graphql`. We already told Codegen where to look for our schemas, but the second filename is up to you.

**The folder structure now looks like this 👇**

```bash
root
--- .next
--- pages
--- public
--- styles
--- graphql 👈 here 🤩
```

Then inside of the file `get-characters.query.graphql`, add the query that we previously used, but without wrapping it in a `gql` template string, like this:

```tsx
query GetCharacters {
  characters {
    results {
      id
      name
      species
    }
  }
}
```

And then re-run the `generate` script, and you will see the following:

```bash
yarn generate
yarn run v1.22.15
$ graphql-codegen --config ./graphql.config.yml
(node:16009) ExperimentalWarning: stream/web is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
✔ Parse Configuration
❯ Generate outputs
✔ Parse Configuration
✔ Generate outputs
✨  Done in 7.43s.
```

Congratulations! We are almost done. Now, check `root` and you will notice that the script has created a new folder called `generated` with a file called `graphql.ts`. This is as we configured in the `graphql.config.file`.

```bash
root
--- .next
--- pages
--- public
--- styles
--- graphql 
--- generated 👈 here 👀
```

If you inspect that file you can see that we have generated a bunch of types pulled from the API and generated custom React hooks for the query that we authored in `get-characters.query.graphql`.

## GraphQL Codegen in Action

Now that you have Graphql Codegen set up and configured, you can start using it in your React application. Let’s modify `index.tsx`to the following:

```tsx
import {
  GetCharactersDocument,
  GetCharactersQuery,
  GetCharactersQueryVariables,
} from "@/generated/graphql";
import { useQuery } from "@apollo/client";
import { NextPage } from "next";

const Home: NextPage = () => {
  const { loading, data } = useQuery<
    GetCharactersQuery,
    GetCharactersQueryVariables
  >(GetCharactersDocument);

  const characters = data?.characters?.results;

  return (
    <div>
      <main>
        {loading && <p>Loading...</p>}

        {characters &&
          characters.map((character, index) => (
            <p key={character?.id ?? index}>
              {character?.name ?? "No name: something is wrong"}
            </p>
          ))}
      </main>
    </div>
  );
};

export default Home;
```

And now, our data is type-safe and validated. If there was something wrong within the query that didn’t match the API, it would have thrown an error during generation. Now, inside our component, we now have fully typed data, which can be demonstrated by hovering over `character`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftmp7xkh92d4nxf62np0.png)

But where are the custom hooks?

Let’s take it one step further and use the `typescript-react-apollo` plugin we told GraphQL Codegen to use. This will make our code *super* clean.

## Using Generated End-To-End Type-Safe Apollo Hooks

Let’s clean up our code a little bit to something like this:

```tsx
import { NextPage } from "next";
import { useGetCharactersQuery } from "@/generated/graphql";

const Home: NextPage = () => {
  // This hook is validated towards the API & will infer 
  // types and throw you type errors if you have not
  // provided the right variables.
  const { loading, data } = useGetCharactersQuery();

  const characters = data?.characters?.results;

  return (
    <div>
      <main>
        {loading && <p>Loading...</p>}

        {characters &&
          characters.map((character, index) => (
            <p key={character?.id ?? index}>
              {character?.name ?? "No name: something is wrong"}
            </p>
          ))}
      </main>
    </div>
  );
};

export default Home;
```

Look at how neat and clean that is! 🤩 And it’s still typed, too!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spp3o8zgm1iufya54tcv.png)

For this project to continue API integration, all developers need to do is add new files into the `graphql` folder and run `yarn generate` before restarting the dev server. Then, everything gets generated automatically.

One more check to see if it works. **Don’t forget to start your development server if you stopped it.**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3asr2rsdr4vlmyr99gj.png)

Obviously, this is a very small app so far. Even so, I think I have been able to demonstrate that even at this scale, GraphQL Codegen is a very valuable tool. 

## **Best Practices for GraphQL Codegen**

There are some best practices that developers should follow when using GraphQL Codegen:

First, it is important to keep your GraphQL Codegen configuration file up-to-date. This will ensure that the generated code is always accurate. It’s also important to make sure your configuration file is well-structured and easy to understand. This makes it easier for changes in the future.

Second, for production projects, it is important to **test your generated code**. This ensures the code is working as expected and any errors are caught before they cause problems in production.

## Adding In New Functionality With Novu

Showing data is pretty neat, but what if we could aggregate that data and send it to our users?

**For this, we can use Novu!**

Novu is an [open-source notification infrastructure](https://novu.co/) for developers. They make it possible to send notifications though a unified API. With their platform, it’s possible to bundle notification sending into “triggers”. 

Managing all notification handlers normally becomes a big chore for us developers as our applications become more complex. Only handling code for email notifications might be doable in a small team, but adding in SMS and push notifications quickly becomes a time drain to get up and running. Which is exactly what Novu intends to help with.

## **How to Get Started with Novu**

The quickest way to get started with Novu is by [signing up for their cloud platform](https://web.novu.co/auth/signup). Its totally free, and with a generous free tier of 10k events per month, you’ll have plenty to work with. There is also the option to self-host the platform on your own servers but will require extra work. For this tutorial, we’ll be going with the simpler option through Novu’s cloud platform.

### Step 1: Sign Up

After signing up they will ask for an organization name, and then you will be greeted with a welcome screen.

### Step 2: Connect the Novu Email Provider

For most providers, Novu sits between them and your application. That means if you are sending an SMS to your user, Novu doesn’t send that, it’s done by your provider. 

However, to get started, I recommend using their built-in email provider. It lets you send up to 300 emails, which is plenty for small applications like ours. The emails will come from *no-reply@novu.co,* and the sending will be the organization name you picked earlier. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtogepx1gsu4wyujb5mn.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jp03prkvruim1svx2xt.png)

### Step 3: Create a New Notification Trigger

We also need to define a trigger by going to **Notifications** in the cloud panel.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/457059yasabje3wsm8tm.png)

Press **New** and add our trigger.

This is what our settings should look like, but feel free to switch it up if you want:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0440u3l15lfxt8xpxbml.png)

Then head into the **Workflow Editor** and configure what happens when triggered.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i32m8g3ez437ns3cywtl.png)

Add an email field and then enter the email editor to edit the template or any other settings you’d like to add.

Email templates often inject variables with `{{ VARIABLE }}`. This is how we’ll inject the request payload into our email template. Apply variables in the editor, and you’ll see them show up in the variables box. In this case, `{{ name }}` and `{{ species }}` will need to be present in the payload we send to the API. This is what our template should look like:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpn579phubkom76h3b7i.png)

Press update and see that the payload variables show up in the variables box:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gn3i4d0nifcq52jk209r.png)

That’s all we need for now! Let’s continue with writing the components and wire everything together. 🪡

### Step 4: Create the React Form Component

Let's jump back into the code and create a form to take our users’ email and store it locally.

We will create a folder in `root` called `components`, with a file called `EmailForm.tsx`.

```tsx
import { FormEvent, useState } from "react";

const EmailForm = () => {
  const [success, setSuccess] = useState<boolean>(false);
  const [email, setEmail] = useState<string>("");

  const onSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    // send the email to user
  };

  return (
    <div>
      <p>Send me random character 🚀</p>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <button type="submit">Send!</button>
      </form>
    </div>
  );
};

export default EmailForm;
```

With our form set up and ready to go, we can connect it to the Novu API!

### Step 5: Install Novu-client

The easiest way to interact with the Novu API is to install their client package. We can install the `@novu/node` package from npm by running `yarn add @novu/node`.

After this, create a folder in `root` called `lib` which will hold the logic for interacting with the Novu API. We’ll call it `novu.ts`, and it’s going to export an async function which we can then re-use.

Then, we’ll head into the **Novu Dashboard** and grab this Node.js code snippet:

```tsx
import { Novu } from '@novu/node'; 

const novu = new Novu('<API_KEY>');

novu.trigger('randm-random-email', {
    to: {
      subscriberId: '<REPLACE_WITH_DATA>',
      email: '<REPLACE_WITH_DATA>'
    },
    payload: {
      name: '<REPLACE_WITH_DATA>',
      species: '<REPLACE_WITH_DATA>'
    }
  });
```

Pretty neat! They’ve done most of the work for us ☕️. Novu adds the payload variables and everything, saving us time from going to the docs.

Now back to the editor:

```tsx
// lib/novu.ts
import { Novu } from "@novu/node";

const novu = new Novu("<API_KEY>");

type Payload = {
  name: string;
  species: string;
};
export const sendEmail = async (email: string, payload: Payload) => {
  if (!email) throw new Error("No email");

  await novu.trigger("<REPLACE_WITH_TRIGGER_ID>", {
    to: {
			email,
      subscriberId: email,
    },
    payload,
  });
};
```

This is how the payload should look in the new `Payload` type, which will be sent to Novu and injected into our email template. Replace `API_KEY` and `TRIGGER_ID` with our variables, and we should be good to go!

*Note: Before pushing this to git, please make sure you’re using environmental variables, not what’s shown in this tutorial.*

### Step 6: Create a Next.js API Endpoint

[Next.js](https://nextjs.org/) is a full-stack framework for React, and they make it easy for us to create full-stack apps. One of the most notable features is that we can create API endpoints on our server. These are then hidden from the client browser for extra security.

The way these works is by adding a new file to our `pages` folder and using the folder structure to define our endpoints. The below structure will create an endpoint at `/send-email` :

```tsx
root
--- .next
--- pages
		--- api
				--- hello.ts
				--- send-email.ts
--- public
--- styles
--- graphql 
--- generated

```

And here’s the code for the handler, where we get our parameters from the request:

```tsx
import { sendEmail } from "@/lib/novu";
import { NextApiRequest, NextApiResponse } from "next";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  // we are only handling post requests.
  if (req.method === "POST") {
    try {
      const { email, name, species } = JSON.parse(req.body);

      if (!email || !name || !species) {
        // return bad request status.
        res.status(400).end();
        return;
      }

      await sendEmail(email, { name, species });

      res.status(200).end();
    } catch (error) {
      // Just response internal server error;
      res.status(500).end();
    }
  }
};

export default handler;
```

This handler will:

1. Look at any incoming `POST` requests to the endpoint 
2. Check if the body of the request has the necessary params 
3. Send the email if everything is correct, or return an error if something is wrong

And that’s all we need for our backend! So let’s add in the client-side.

### Step 7: Add Our Email Functionality

Now that we’re back at the client-side, let’s add in the rest of the functionality. We’ll start at the top of the tree in `index.tsx` and import our `EmailForm.tsx` component like so:

```tsx
import { NextPage } from "next";
import { useGetCharactersQuery } from "@/generated/graphql";
import EmailForm from "@/components/EmailForm";

const Home: NextPage = () => {
  const { loading, data } = useGetCharactersQuery();

  const characters = data?.characters?.results;

  return (
    <div>
      <main>
        {loading && <p>Loading...</p>}

        {characters && (
          <div>
            <EmailForm {...{ characters }} />

            {characters.map((character, index) => (
              <p key={character?.id ?? index}>
                {character?.name ?? "No name: something is wrong"}
              </p>
            ))}
          </div>
        )}
      </main>
    </div>
  );
};

export default Home;
```

That was simple! Let’s finish the implementation in `EmailForm.tsx` :

```tsx
import { Character } from "@/generated/graphql";
import { FormEvent, useState } from "react";

type Props = {
  characters: (Character | null)[] | null | undefined;
};

const EmailForm: React.FC<Props> = ({ characters }) => {
  const [email, setEmail] = useState<string>("");

  const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (characters && characters?.length > 0) {
      // Get random character from the array.
      const random = Math.floor(Math.random() * (characters.length - 1));
      const randomCharacter = characters[random];
      // Make send email request to /api/send-email
      fetch("http://localhost:3000/api/send-email", {
        method: "POST",
        body: JSON.stringify({
          email,
          name: randomCharacter?.name,
          species: randomCharacter?.species,
        }),
      });
    }
  };

  return (
    <div>
      <p>Send me random character 🚀</p>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <button type="submit">Send!</button>
      </form>
    </div>
  );
};

export default EmailForm;
```

This form component will:

1. Render a text input for email, and store the data in local storage
2. Submit the data when the button has been pressed
3. Makes a request to our API which sends a request to [Novu.co](http://Novu.co), which in turn will send en email to the given email address

And that’s it! Now we need to test it out.

### Step 8: Send Emails!

If we take a look in the browser at this point, it should look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nyfcgs4vm0jwr4x7mrg.png)

It’s not pretty, but it’s functional!

Enter your email and see if it shows up in your inbox!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/azxoqgrg0p3sgmys6bm6.png)

## Wrapping It Up

Integrating APIs into a project can be difficult, but it doesn’t have to be. And with GraphQL, it’s easier than ever before! It allows you to set up API integrations with ease and can pair with tools like GraphQL Codegen to automate some of the hassles. 

Paired with a powerful notification platform like Novu, you can easily get a project up and running in no time, with notifications and communication options at your disposal. 

And the best part? You can do it all for free! No buy-in required. I know I’ll be using Novu in my future, what about you?

*Full source code can be found here [https://github.com/ugglr/next-typescript-graphql-integration](https://github.com/ugglr/next-typescript-graphql-integration)*