Build a GraphQL app in Node.js with TypeScript and graphql-request
Tags: graphql, node, typescript
Published: 2022-06-09 14:34:01

**Written by [Hussain Arif](https://blog.logrocket.com/author/hussain-arif/)✏️**

In this article, you will build a full-stack app using GraphQL and Node.js in the backend. Meanwhile, our frontend will use the [`graphql-request`](https://github.com/prisma-labs/graphql-request) library to perform network operations on our backend. We will cover the following steps:

*   [Why use graphql-request and TypeScript?](#why-use-graphql-request-and-typescript)
*   [Building our server](#building-our-server)
    *   [Project initialization](#project-initialization)
    *   [Creating our database](#creating-our-database)
    *   [Defining our schema](#defining-our-schema)
    *   [Creating resolvers](#creating-resolvers)
    *   [Configuring our server](#configuring-our-server)
    *   [Creating mutations](#creating-mutations)
*   [Building our client](#building-our-client)
    *   [Creating query variables](#creating-query-variables)
    *   [Performing queries](#performing-queries)
    *   [Performing mutations](#performing-mutations)

## Why use `graphql-request` and TypeScript? <a name="why-use-graphql-request-and-typescript">

Whenever developers build a GraphQL server using Apollo, the library generates a “frontend” which looks like so: ![Frontend Developed By GraphQL And Apollo](https://blog.logrocket.com/wp-content/uploads/2022/05/frontend-developed-by-graphql-and-apollo.png) 

This interface allows users to make query or mutation requests to the server via code. However, let’s address the elephant in the room: it doesn’t look very user friendly. Since the frontend doesn’t feature any buttons or any helpful interface elements, it might be hard for many users to navigate around your app. Consequently, this shrinks your user base. So how do we solve this problem? 

This is where [`graphql-request`](https://github.com/prisma-labs/graphql-request) comes in. It is an open source library which lets users perform queries on a GraphQL server. It boasts the following features:

*   Lightweight — This library is just over 21 kilobytes minified, which ensures your app stays performant
*   Promise-based API — This brings in support for [asynchronous applications](https://stackoverflow.com/questions/56816731/what-are-the-advantages-of-using-async-await)
*   TypeScript support — `graphql-request` is one of many libraries which allows for TypeScript. One major advantage of Typescript is that it allows for stable and predictable code

For example, look at the following program:

```javascript
let myNumber = 9; //here, myNumber is an integer
myNumber = 'hello'; //now it is a string.
myNumber = myNumber + 10; //even though we are adding a string to an integer,
//JavaScript won't return an error. In the real world, it might bring unexpected outputs.
//However, in Typescript, we can tell the compiler..
//what data types we need to choose.
let myNumber:number = 39; //tell TS that we want to declare an integer.
myNumber = 9+'hello'; //returns an error. Therefore, it's easier to debug the program
//this promises stability and security. 
```

In this article, we will build a full-stack app using GraphQL and TypeScript. Here, we will use the [`apollo-server-express`](https://www.npmjs.com/package/apollo-server-express) package to build a backend server. Furthermore, for the frontend, we will use Next and `graphql-request` to consume our GraphQL API.

## Building our server

### Project initialization

To initialize a blank Node.js project, run these terminal commands:

```javascript
mkdir graphql-ts-tutorial #create project folder 
cd graphql-ts-tutorial 
npm init -y #initialize the app
```

When that’s done, we now have to tell Node that we need to use TypeScript in our codebase:

```javascript
#configure our Typescript:
npx tsc --init --rootDir app --outDir dist --esModuleInterop --resolveJsonModule --lib es6 --module commonjs --allowJs true --noImplicitAny true
mkdir app #our main code folder
mkdir dist #Typescript will use this folder to compile our program.
```

Next, install these dependencies:

```typescript
#development dependencies. Will tell Node that we will use Typescript
npm install -d ts-node @types/node typescript @types/express nodemon
#Installing Apollo Server and its associated modules. Will help us build our GraphQL
#server
npm install apollo-server-express apollo-server-core express graphql
```

After this step, navigate to your `app` folder. Here, create the following files:

*   `index.ts`: Our main file. This will execute and run our Express GraphQL server
*   `dataset.ts`: This will serve as our database, which will be served to the client
*   `Resolvers.ts`: This module will handle user commands. We will learn about resolvers later in this article
*   `Schema.ts`: As the name suggests, this file will store the schematics needed to send data to the client

In the end, your folder structure should look like so: ![Folder Structure](https://blog.logrocket.com/wp-content/uploads/2022/05/folder-structure.png)

### Creating our database

In this section, we will create a dummy database which will be used to send requested data. To do so, go to `app/dataset.ts` and write the following code:

```typescript
let people: { id: number; name: string }[] = [
  { id: 1, name: "Cassie" },
  { id: 2, name: "Rue" },
  { id: 3, name: "Lexi" },
];
export default people;
```

*   First, we created an array of objects called `people`
*   This array will have two fields: `id` of type `number`, and `name` of type `string`

### Defining our schema

Here, we will now create a schema for our GraphQL server. 

To put it simply, a GraphQL schema is a description of the dataset that clients can request from an API. This concept is similar to that of the [Mongoose library](https://mongoosejs.com/). To build a schema, navigate to the `app/Schema.ts` file. There, write the following code:

```typescript
import { gql } from "apollo-server-express"; //will create a schema
const Schema = gql`
  type Person {
    id: ID!
    name: String
  }
  #handle user commands
  type Query {
    getAllPeople: [Person] #will return multiple Person instances
    getPerson(id: Int): Person #has an argument of 'id` of type Integer.
  }
`;
export default Schema; 
//export this Schema so we can use it in our project
```

Let’s break down this code piece by piece:

*   The `Schema` variable contains our GraphQL schema
*   First, we created a `Person` schema. It will have two fields: `id` of type `ID` and `name` of type `String`
*   Later on, we instructed GraphQL that if the client runs the `getAllPeople` command, the server will return an array of `Person` objects
*   Furthermore, if the user uses the `getPerson` command, GraphQL will return a single `Person` instance

### Creating resolvers

Now that we have coded our schema, our next step is to define our resolvers. In simple terms, a resolver is a group of functions that generate response for a GraphQL query. In other words, a resolver serves as a GraphQL query handler. 

In `Resolvers.ts`, write the following code:

```typescript
import people from "./dataset"; //get all of the available data from our database.
const Resolvers = {
  Query: {
    getAllPeople: () => people, //if the user runs the getAllPeople command
    //if the user runs the getPerson command:
    getPerson: (_: any, args: any) => { 
      console.log(args);
      //get the object that contains the specified ID.
      return people.find((person) => person.id === args.id);
    },
  },
};
export default Resolvers;
```

*   Here, we created a `Query` object that handles all the incoming queries going to the server
*   If the user executes the `getAllPeople` command, the program will return all the objects present in our database
*   Moreover, the `getPerson` command requires an argument `id`. This will return a `Person` instance with the matching ID
*   In the end, we exported our resolver so that it could be linked with our app

### Configuring our server

We’re almost done! Now that we have built both our schema and resolver, our next step is to link them together. In `index.js`, write this block of code:

```javascript
import { ApolloServer } from "apollo-server-express";
import Schema from "./Schema";
import Resolvers from "./Resolvers";
import express from "express";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
import http from "http";

async function startApolloServer(schema: any, resolvers: any) {
  const app = express();
  const httpServer = http.createServer(app);
  const server = new ApolloServer({
    typeDefs: schema,
    resolvers,
    //tell Express to attach GraphQL functionality to the server
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  }) as any;
  await server.start(); //start the GraphQL server.
  server.applyMiddleware({ app });
  await new Promise<void>((resolve) =>
    httpServer.listen({ port: 4000 }, resolve) //run the server on port 4000
  );
  console.log(`Server ready at http://localhost:4000${server.graphqlPath}`);
}
//in the end, run the server and pass in our Schema and Resolver.
startApolloServer(Schema, Resolvers);
```

Let’s test it out! To run the code, use this Bash command:

```bash
npx nodemon app/index.ts 
```

This will create a server at the `localhost:4000/graphql` URL. 

Here, you can see your available schemas within the UI: ![Available Schemas Within The UI](https://blog.logrocket.com/wp-content/uploads/2022/05/available-schemas-within-ui.gif) 

This means that our code works! All of our GraphQL queries will go within the **Operation** panel. 

To see it in action, type this snippet within this box:

```typescript
#make a query:
query {
  #get all of the people available in the server
  getAllPeople {
    #procure their IDs and names.
    id
    name
  }
}
```

To see the result, click on the **Run** button: ![Run Button For Results](https://blog.logrocket.com/wp-content/uploads/2022/05/run-button-for-results.png) We can even search for a specific entity via the `getPerson` query:

```typescript
query ($getPersonId: Int) { #the argument will be of type Integer
  getPerson(id: 1) {
    #get the person with the ID of 1
    name
    id
  }
}
```

![Getperson Query](https://blog.logrocket.com/wp-content/uploads/2022/05/getperson-query.png)

### Creating mutations

In the GraphQL world, mutations are commands that perform side effects on the database. Common examples of this include:

*   Adding a user to the database — When a client signs up for a website, the user performs a mutation to save their data in their database
*   Editing or deleting an object — If a user modifies or removes data from a database, they are essentially creating a mutation on the server

To handle mutations, go to your `Schema.ts` module. Here, within the `Schema` variable, add the following lines of code:

```typescript
const Schema = gql`
  #other code..
  type Mutation {
    #the addPerson commmand will accept an argument of type String.
    #it will return a 'Person' instance. 
    addPerson(name: String): Person
  }
`;
```

Our next step is to create a resolver to handle this mutation. To do so, within the `Resolvers.ts` file, add this block of code:

```typescript
const Resolvers = {
  Query: {
    //..further code..
  },
  //code to add:
  //all our mutations go here.
  Mutation: {
    //create our mutation:
    addPerson: (_: any, args: any) => {
      const newPerson = {
        id: people.length + 1, //id field
        name: args.name, //name field
      };
      people.push(newPerson);
      return newPerson; //return the new object's result
    },
  },
};
```

*   The `addPerson` mutation accepts a `name` argument
*   When a `name` is passed, the program will create a new object with a matching `name` key
*   Next, it will use the `push` method to add this object to the `people` dataset
*   Finally, it will return the new object’s properties to the client

That’s it! To test it out, run this code within the **Operations** window:

```typescript
#perform a mutation on the server
mutation($name: String) {
  addPerson(name:"Hussain") { #add a new person with the name "Hussain"
    #if the execution succeeds, return its 'id' and 'name` to the user.
    id
    name
  }
}
```

![Addperson](https://blog.logrocket.com/wp-content/uploads/2022/05/addperson.png) Let’s verify if GraphQL has added the new entry to the database:

```typescript
query {
  getAllPeople { #get all the results within the 'people' database. 
  #return only their names
  name 
  }
}
```

![Verify That GraphQL Added A New Entry](https://blog.logrocket.com/wp-content/uploads/2022/05/verify-graphql-added-new-entry.png)

## Building our client

We have successfully built our server. In this section, we will build a client app using Next that will listen to the server and render data to the UI. 

As a first step, initialize a blank Next.js app like so:

```javascript
npx create-next-app@latest graphql-client --ts
touch constants.tsx #our query variables go here.
```

To perform GraphQL operations, we will use the [`graphql-request`](https://github.com/prisma-labs/graphql-request#features) library. This is a minimal, open source module that will help us make mutations and queries on our server:

```javascript
npm install graphql-request graphql
npm install react-hook-form #to capture user input
```

### Creating query variables

In this section, we will code our queries and mutations to help us make GraphQL operations. To do so, go to `constants.tsx` and add the following code:

```typescript
import { gql } from "graphql-request";
//create our query
const getAllPeopleQuery = gql`
  query {
    getAllPeople { #run the getAllPeople command
      id
      name
    }
  }
`;
//Next, declare a mutation
const addPersonMutation = gql`
  mutation addPeople($name: String!) {
    addPerson(name: $name) { #add a new entry. Argument will be 'name'
      id
      name
    }
  }
`;
export { getAllPeopleQuery, addPersonMutation };
```

*   In the first part, we created the `getAllPeopleQuery` variable. When the user runs this query, the program will instruct the server to get all the entries present in the database
*   Later on, the `addPerson` mutation tells GraphQL to add a new entry with its respected `name` field
*   In the end, we used the `export` keyword to link our variables with the rest of the project

### Performing queries

In `pages/index.ts`, write the following code:

```typescript
import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request"; //allows us to perform a request on our server
import { getAllPeopleQuery } from "../constants"; 
import Link from "next/link";
const Home: NextPage = ({
  result, //extract the 'result' prop 
}: InferGetStaticPropsType<typeof getStaticProps>) => {
  return (
    <div className={styles.container}>
      {result.map((item: any) => { //render the 'result' array to the UI 
        return <p key={item.id}>{item.name}</p>;
      })}
    <Link href="/addpage">Add a new entry </Link>
    </div>
  );
};
//fetch data from the server
export const getStaticProps: GetStaticProps = async () => {
  //the first argument is the URL of our GraphQL server
  const res = await request("http://localhost:4000/graphql", getAllPeopleQuery);
  const result = res.getAllPeople;
  return {
    props: {
      result,
    }, // will be passed to the page component as props
  };
};
export default Home;
```

Here is a breakdown of this code piece by piece:

*   In the `getStaticProps` method, we instructed Next to run the `getAllPeople` command on our GraphQL server
*   Later on, we returned its response to the `Home` functional component. This means that we can now render the result to the UI
*   Next, the program used the `map` method to render all of the results of the `getAllPeople` command to the UI. Each paragraph element will display the `name` fields of each entry
*   Furthermore, we also used a `Link` component to redirect the user to the `addpage` route. This will allow the user to add a new `Person` instance to the table

To test out the code, run the following terminal command:

```bash
npm run dev
```

This will be the result: ![Addpage Route](https://blog.logrocket.com/wp-content/uploads/2022/05/addpage-route-1.png) Our GraphQL server even updates in real time. ![GraphQL Updating In Real Time](https://blog.logrocket.com/wp-content/uploads/2022/05/graphql-updating-in-real-time.gif)

### Performing mutations

Now that we have successfully performed a query, we can even perform mutations via the `graphql-request` library. 

Within your `pages` folder, create a new file called `addpage.tsx`. As the name suggests, this component will allow the user to add a new entry to the database. Here, start by writing the following block of code:

```typescript
import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request";
import { addPersonMutation } from "../constants";
const AddPage: NextPage = () => {
  return (
    <div>
      <p>We will add a new entry here. </p>
    </div>
  );
};
export default AddPage;
```

In this piece of code, we are creating a blank page with a piece of text. We are doing this to ensure whether our URL routing system works. ![Creating A Blank Page To Ensure URL Routing Works](https://blog.logrocket.com/wp-content/uploads/2022/05/creating-blank-page-ensuring-url-routing-works.gif) This means that we used routing successfully! Next, write this snippet in your `addpage.tsx` file:

```typescript
import { useForm } from "react-hook-form";
const { register, handleSubmit } = useForm();
//if the user submits the form, then the program will output the value of their input.
const onSubmit = (data: any) => console.log(data);
return (
  <div>
    <form onSubmit={handleSubmit(onSubmit)}> {/*Bind our handler to this form.*/}
      {/* The user's input will be saved within the 'name' property */}
      <input defaultValue="test" {...register("name")} />
      <input type="submit" />
    </form>
  </div>
);
```

This will be the output: ![Output](https://blog.logrocket.com/wp-content/uploads/2022/05/output.gif) 

Now that we have successfully captured the user’s input, our last step is to add their entry to the server. 

To do so, change the `onSubmit` handler located in `pages/addpage.tsx` file like so:

```typescript
const onSubmit = async (data: any) => {
  const response = await request(
    "http://localhost:4000/graphql",
    addPersonMutation,
    data
  );
  console.log(response);
};
```

*   Here, we’re performing a mutation request to our GraphQL server via the `request` function
*   Furthermore, we also passed in the `addPerson` mutation command to our request header. This will tell GraphQL to perform the `addMutation` action on our server

This will be the result: ![Result Of Addmutation Action](https://blog.logrocket.com/wp-content/uploads/2022/05/result-of-addmutation-action.gif) 

And we’re done!

## Conclusion

[Here is the full source code of this project.](https://github.com/HussainArif12/GraphQL-fullstack) 

In this article, you learned how to create a full-stack app using GraphQL and TypeScript. They both are extremely crucial skills within the programming world since they are in high demand nowadays. 

If you encountered any difficulty in this code, I advise you to deconstruct the code and play with it so that you can fully grasp this concept. 

Thank you so much for reading! Happy coding!

---
##200’s only :heavy_check_mark: Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket.](https://logrocket.com/signup/)

[![LogRocket Sign Up](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.