A Simple CRUD app With GraphQL, Apollo Server, MongoDB, and Express
Tags: webdev, node, graphql, javascript
Published: 2024-03-07 05:07:44

## A Simple CRUD app With GraphQL, Apollo Server, MongoDB, and Express
 
It has been said in some parts that learning `GraphQL` can be hard and complex to understand😌😌 But thankfully 😋😋you have me to break it down to you.
In this tutorial I will be taking you step by step, we'll be creating a blog app where you can post the title and content of a blog, update the blog, and also get all blogs.  I strongly believe that when you understand basic CRUD operations with these stacks you can build on the knowledge you have🥰..

Let's Dive right In

![Dive](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xsfifjkmapuqinopthe7.gif)

## Step 1 Project Setup

Create a new directory for your project and initialize it with npm:
```bash
mkdir graphql-blog-app
cd graphql-blog-app
npm init -y
```

## Step 2: We'll Install Our Project Dependencies
```bash
npm install express mongoose graphql apollo-server-express
```
## Step 3: We'll Create Our Entry File, dotenv and gitignore file
```bash
touch index.js .env .gitignore
```
## Step 4: Let's edit our `index.js` file
```javascript
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const mongoose = require('mongoose');

const app = express();

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/blog-app-graphql'); // Replace with your MongoDB connection string
mongoose.connection.once('open', () => {
  console.log('Connected to MongoDB');
});

// Use the `start` method to start the Apollo Server
async function startApolloServer() {
  await server.start();

  // Apply middleware after the server has started
  server.applyMiddleware({ app });

  const PORT = 3000; // Define the port for your Express server
  app.listen(PORT, () => {
    console.log(`Server listening on http://localhost:${PORT}${server.graphqlPath}`);
  });
}

// Start the Apollo Server
startApolloServer();
```

## Step 5: We're going to create our post models(Mongoose Schema). Create a `models` folder and a `Post.js` file and edit it
```javascript
// models/Post.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
  },
  content: {
    type: String,
    required: true,
  },
});

const Post = mongoose.model('Post', postSchema);

module.exports = Post;
```
## Step 6: Now we'll create our Graphql schema. so create a `schema.js` file
```javascript
const { gql } = require('apollo-server-express');

// Define the GraphQL schema using the gql template literal
const typeDefs = gql`
  # Post type represents a blog post
  type Post {
    id: ID!             # Unique identifier for the post
    title: String!      # Title of the post, non-nullable
    content: String!    # Content of the post, non-nullable
  }

  # Query type defines the available queries for fetching data
  type Query {
    posts: [Post]       # Query to get a list of all posts
    post(id: ID!): Post  # Query to get a specific post by ID
  }

  # Mutation type defines the available mutations for modifying data
  type Mutation {
    createPost(title: String!, content: String!): Post    # Mutation to create a new post
    updatePost(id: ID!, title: String, content: String): Post  # Mutation to update an existing post
    deletePost(id: ID!): Post                              # Mutation to delete a post
  }
`;

module.exports = typeDefs;
```
I'll break it down a little further🥰
1. Post Type:
A "Post" is like a blog article.
It has a unique ID, like a special number that is different for every post.
It also has a title, which is like the name of the blog post.
The content is what's written inside the blog post.
Query:

2. Think of a "Query" as a question you ask the computer to get information.
We have two questions:
* "posts" asks for a list of all the blog posts.
* "post" asks for a specific blog post, and we need to tell the computer which one by giving it the post's special ID.
Mutation:

3. A "Mutation" is like giving the computer a task to change something.
We have three tasks:
* "createPost" is like telling the computer to create a new blog post. We need to give it a title and content.
* "updatePost" is for changing the title or content of an existing blog post. We need to say which post (by its ID) and what we want to change.
* "deletePost" is for removing a blog post. Again, we need to tell the computer which post to delete.

## Step 7: Now we'll create our resolvers, so you'll create `resolvers.js` file
```javascript
// Import the Post model, assuming it's a file in the same directory
const Post = require('./models/Post');

// Resolvers define how to respond to GraphQL queries and mutations

const resolvers = {
  // Query resolver object handles read operations (fetching data)
  Query: {
    // Resolver for fetching all posts
    posts: async () => {
      try {
        // Use the Post model to find and return all posts
        return await Post.find();
      } catch (error) {
        // If there's an error, throw an informative error message
        throw new Error('Error fetching posts from the database');
      }
    },
    // Resolver for fetching a post by ID
    post: async (parent, { id }) => {
      try {
        // Use the Post model to find and return a specific post by ID
        return await Post.findById(id);
      } catch (error) {
        // If there's an error, throw an informative error message
        throw new Error(`Error fetching post with ID: ${id} from the database`);
      }
    },
  },

  // Mutation resolver object handles write operations (create, update, delete)
  Mutation: {
    // Resolver for creating a new post
    createPost: async (parent, { title, content }) => {
      try {
        // Create a new post instance using the Post model
        const post = new Post({ title, content });
        // Save the new post to the database and return it
        return await post.save();
      } catch (error) {
        // If there's an error, throw an informative error message
        throw new Error('Error creating a new post in the database');
      }
    },

    // Resolver for updating an existing post
    updatePost: async (parent, { id, title, content }) => {
      try {
        // Find and update a post by ID, return the updated post
        return await Post.findByIdAndUpdate(id, { title, content }, { new: true });
      } catch (error) {
        // If there's an error, throw an informative error message
        throw new Error(`Error updating post with ID: ${id} in the database`);
      }
    },

    // Resolver for deleting a post by ID
    deletePost: async (parent, { id }) => {
      try {
        // Find and delete a post by ID, return the deleted post
        return await Post.findByIdAndDelete(id);
      } catch (error) {
        // If there's an error, throw an informative error message
        throw new Error(`Error deleting post with ID: ${id} from the database`);
      }
    },
  },
};

// Export the resolvers for use in the Apollo Server
module.exports = resolvers;
```
## Step 8: Now we'll import our typedefs and resolvers into our `index.js` file
```javascript
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const mongoose = require('mongoose');
const typeDefs = require('./schema'); // Import your GraphQL schema definition
const resolvers = require('./resolvers'); // Import your GraphQL resolvers

const app = express();

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/blog-app-graphql'); // Replace with your MongoDB connection string
mongoose.connection.once('open', () => {
  console.log('Connected to MongoDB');
});

// Create an instance of ApolloServer with your schema and resolvers
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// Use the `start` method to start the Apollo Server
async function startApolloServer() {
  await server.start();

  // Apply middleware after the server has started
  server.applyMiddleware({ app });

  const PORT = 3000; // Define the port for your Express server
  app.listen(PORT, () => {
    console.log(`Server listening on http://localhost:${PORT}${server.graphqlPath}`);
  });
}

// Start the Apollo Server
startApolloServer();
```
UUUUuuuhhhhhhhh and now we're done.. Now We TEST

![uuggh](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9in0tgjvad2wv92okhv3.gif)

## Step 9: We start the server
```bash
node index.js
```
> You should get this `Server listening on http://localhost:3000/graphql
Connected to MongoDB` 

Now go to that route `http://localhost:3000/graphql` on your browser this would open up the Appolo server
> You'll see the button `Query Your Server`
Let's create a post by making a mutation
```graphql
mutation AddBlogMutation{
  createPost(title: "My Dev.to Blog", content: "My Dev Blog Content") {
    id
    title
    content
  }
}
```

![First Mutation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spid6l7hp64ghh39p0ef.png)
> Let's update the post 
We'll copy the id from the previous mutation
```graphql
mutation updateBlog{
  updatePost(id: "65e946a3bf3014e3120fb1d8", title: "Dev Updated Title", content: "Dev Updated Content") {
    id
    title
    content
  }
}
```

![Update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8tsg0a44f9vz3g3m1ob1.png)
> Now let's get all post we make a posts query
```graphql
query getAllPost{
  posts {
    id
    title
    content
  }
}
```
![Get All Post](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bak7h6aq690zj6ysekvu.png)
> Get Post Id
```graphql
query getPostById{
  post(id: "65e6ccbcb42b8a669ac890e3") {
    id
    title
    content
  }
}
```
> Delete Post Mutation
```graphql
mutation deleteMutation{
  deletePost(id: "65e6cc18b42b8a669ac890dd") {
    id
    title
    content
  }
}
```
## And now we're done😎😎😎. Isn't this interesting. I hope this article has been helpful. Here is the [Github repo for the article](https://github.com/FredAbod/GraphQL-CRUD-app)...
Don't forget to hit the like 🥰🥰 button and follow...


![Bow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dz2e4k6mkdnh3rcfhu4q.gif)