GraphQL with NEXT JS
Tags: javascript, nextjs, graphql, mongodb
Published: 2023-06-15 13:04:26

Hello Everyone Today i will be discussing GraphQL API with Next JS.

GraphQL is an open-source query language and runtime for APIs (Application Programming Interfaces). It provides a flexible and efficient approach for clients to request and manipulate data from a server. GraphQL was developed by Facebook and released to the public in 2015.

Unlike traditional REST APIs, where clients often receive fixed data structures determined by the server, GraphQL allows clients to define the shape and structure of the data they need. Clients can send a single request to the server, specifying the exact data requirements, and receive a response that matches the requested structure. This eliminates the problem of over-fetching or under-fetching data, where clients receive more or less data than they actually need.

Here's a step-by-step guide to performing CRUD operations with GraphWL, Express JS, MongoDB, and NEXT JS - 

### This will be our folder structure
```
- project/
  - components/
    - Books.js
  - pages/
    - index.js
  - apolloClient.js
  - server.js
  - package.json
```

We will Create a Simple Book Storing APP for Adding, updating, fetching, and deleting a book.

## Step 1: Setting up the Next.js project

* Create a new directory for your Next.js project and navigate into it.
* Initialize a new Next.js project by running the following command:
```
npx create-next-app graphql-next-app
```
Go into that directory
```
cd graphql-next-app
```

* Install the required dependencies
```
npm install express mongoose next apollo-server-express graphql apollo-client apollo-link-http graphql
```

## Step 2: Setting up the server

* Create a new file called server.js and add the following code:

```js
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const mongoose = require('mongoose');

// Connect to MongoDB
mongoose.connect('mongodb://localhost/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true });

const app = express();

const typeDefs = gql`
  type Book {
    id: ID!
    title: String!
    author: String!
  }

  type Query {
    books: [Book]
    book(id: ID!): Book
  }

  type Mutation {
    createBook(title: String!, author: String!): Book
    updateBook(id: ID!, title: String, author: String): Book
    deleteBook(id: ID!): Boolean
  }
`;

const books = [
  { id: '1', title: 'Book 1', author: 'Author 1' },
  { id: '2', title: 'Book 2', author: 'Author 2' },
];

const resolvers = {
  Query: {
    books: () => books,
    book: (parent, { id }) => books.find((book) => book.id === id),
  },
  Mutation: {
    createBook: (parent, { title, author }) => {
      const newBook = { id: String(books.length + 1), title, author };
      books.push(newBook);
      return newBook;
    },
    updateBook: (parent, { id, title, author }) => {
      const book = books.find((book) => book.id === id);
      if (!book) {
        throw new Error('Book not found');
      }
      book.title = title || book.title;
      book.author = author || book.author;
      return book;
    },
    deleteBook: (parent, { id }) => {
      const bookIndex = books.findIndex((book) => book.id === id);
      if (bookIndex === -1) {
        throw new Error('Book not found');
      }
      books.splice(bookIndex, 1);
      return true;
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.applyMiddleware({ app });

app.listen({ port: 4000 }, () =>
  console.log(`Server ready at http://localhost:4000${server.graphqlPath}`)
);
```
* The code imports the express, ApolloServer, gql (GraphQL tag), and mongoose libraries using the require function. These libraries are required for setting up the server, defining GraphQL schemas, and connecting to MongoDB.
* Then we connect to the mongo db database using mongoose
* We have created an instance of the Express application using app = express()
* The typeDefs gql function is used to define the GraphQL type definitions. It defines a Book type with id, title, and author fields. It also defines Query and Mutation types with corresponding operations (books, book, createBook, updateBook, deleteBook) and their input parameters.
* An array books is defined to store some initial book data. This data will be used in the resolvers.
* The resolvers object defines the resolver functions for the defined queries and mutations. Resolvers handle the logic for fetching data for queries and performing actions for mutations. In this case, the resolvers provide functions to fetch and manipulate the books array.
* The ApolloServer class is instantiated with the type definitions (typeDefs) and resolvers (resolvers) provided as configuration options. It will create an Apollo Server to serve the API.
* Then we started the server at port:4000.

## Step 3: Start the server

Run the following command to start the server
```
node server.js
```
## Step 4: Creating the GraphQL client

* Create a new file called apolloClient.js in the root of your Next.js project.
* Add the following code to create the Apollo Client:

```js
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';

const client = new ApolloClient({
  link: new HttpLink({
    uri: 'http://localhost:4000/graphql', // Update with your server URL
  }),
  cache: new InMemoryCache(),
});

export default client;
```

## Step 5: Creating a component to perform CRUD operations

* Create a new file called Books.js in the components folder (create the folder if it doesn't exist).
* Add the following code to the Books.js component:

```js
import { useQuery, useMutation, gql } from '@apollo/client';

const GET_BOOKS = gql`
  query {
    books {
      id
      title
      author
    }
  }
`;

const CREATE_BOOK = gql`
  mutation CreateBook($title: String!, $author: String!) {
    createBook(title: $title, author: $author) {
      id
      title
      author
    }
  }
`;

const UPDATE_BOOK = gql`
  mutation UpdateBook($id: ID!, $title: String, $author: String) {
    updateBook(id: $id, title: $title, author: $author) {
      id
      title
      author
    }
  }
`;

const DELETE_BOOK = gql`
  mutation DeleteBook($id: ID!) {
    deleteBook(id: $id)
  }
`;

const Books = () => {
  const { loading, error, data } = useQuery(GET_BOOKS);
  const [createBook] = useMutation(CREATE_BOOK);
  const [updateBook] = useMutation(UPDATE_BOOK);
  const [deleteBook] = useMutation(DELETE_BOOK);

  const handleCreateBook = async () => {
    try {
      const { data } = await createBook({ variables: { title: 'New Book', author: 'New Author' } });
      console.log('New book created:', data.createBook);
    } catch (error) {
      console.error('Error creating book:', error);
    }
  };

  const handleUpdateBook = async (id, title, author) => {
    try {
      const { data } = await updateBook({ variables: { id, title, author } });
      console.log('Book updated:', data.updateBook);
    } catch (error) {
      console.error('Error updating book:', error);
    }
  };

  const handleDeleteBook = async (id) => {
    try {
      const { data } = await deleteBook({ variables: { id } });
      console.log('Book deleted:', data.deleteBook);
    } catch (error) {
      console.error('Error deleting book:', error);
    }
  };

  if (loading) {
    return <p>Loading...</p>;
  }

  if (error) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <div>
      <button onClick={handleCreateBook}>Create Book</button>
      <ul>
        {data.books.map((book) => (
          <li key={book.id}>
            {book.title} by {book.author}{' '}
            <button onClick={() => handleUpdateBook(book.id, 'Updated Title', 'Updated Author')}>
              Update
            </button>{' '}
            <button onClick={() => handleDeleteBook(book.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Books;
```
* This component uses the Apollo Client's useQuery, useMutation hooks to fetch the books data, create a new book, update an existing book, and delete a book.
* So Using gql, we have created some queried and stored those queries in variables.
* We also destructured the method from our queries variables to use it with the handler's methods
* Then we have created async methods to handle CRUD operation for our BOOK app and wrapped those inside try/catch block
* Inside variable Object, we are passing the data we want to save, update, delete, or fetch.
* Then we are checking loading and error states.
* We then created a Button and attached the handleCreateBook Method to save a book.
* Finally we have mapped the Book data we got from destructuring using the useQuery hook. Each Book has an Edit and Delete button calling their respective methods.

## Step 6: Updating the Next.js pages

Open the pages/index.js file and replace its contents with the following code:

```js
import Books from '../components/Books';

const Home = () => {
  return (
    <div>
      <h1>Books</h1>
      <Books />
    </div>
  );
};

export default Home;
```

* Run the server using -
```
npm run dev
```

* Now you can visit http://localhost:3000 in your browser to see the Next.js application with the ability to perform CRUD operations on the GraphQL API.

### Note: Make sure your GraphQL server is running on http://localhost:4000 (as specified in the apolloClient.js file) for the Next.js application to connect to it successfully.

Congratulations you have created a basic Full Stack app with GraphQL, Express JS, MongoDB, and Next JS.

THANK YOU FOR CHECKING THIS POST
You can contact me on -
Instagram - https://www.instagram.com/supremacism__shubh/
LinkedIn - https://www.linkedin.com/in/shubham-tiwari-b7544b193/
Email - shubhmtiwri00@gmail.com

^^You can help me with some donation at the link below Thank you👇👇 ^^
☕ --> https://www.buymeacoffee.com/waaduheck <--

Also check these posts as well
https://dev.to/shubhamtiwari909/website-components-you-should-know-25nm

https://dev.to/shubhamtiwari909/smooth-scrolling-with-js-n56

https://dev.to/shubhamtiwari909/swiperjs-3802

https://dev.to/shubhamtiwari909/custom-tabs-with-sass-and-javascript-4dej