DEV Community

Cover image for How to build a blog app with MongoDB, NextJS, and Buildable
Esther Adebayo for IntegrationOS

Posted on

How to build a blog app with MongoDB, NextJS, and Buildable

In this tutorial, we'll build a blog application that allows users to create new blog posts and list all posts on a homepage. The purpose of this post is to show you how to leverage Buildable and MongoDB to build your blog. We'll dive into how easy it is to generate API endpoints in Buildable and explore your MongoDB Atlas within Buildable's IDE.

Here's a sneak peek of the blog homepage we'll create together.

Blog home page

Prerequisites

For this tutorial, you'll need the following:

  • NextJS app - To create the frontend of our blog application
  • MongoDB Atlas - To manage our database
  • Buildable Account - To create REST endpoints (Create your free account here)
  • Chakra UI (Optional) - To style our app

Step 1: Bootstrapping a NextJS app

To bootstrap a NextJS app, you'd need to run this command in your terminal window:

npx create-next-app@latest

You'll be prompted to give your app a name. Let's call it "blog-app." Once this installation is complete, navigate to this directory by running cd blog-app.

At this point, go ahead to install and set up the styling or component library of your choice. For this tutorial, we'll be using Chakra UI.

Now, let's start up our local development server. To do this, we'll run yarn dev.

Navigate to localhost:3000 and you should see this

NextJS getting started

Step 2: Connecting your MongoDB Atlas

Now that we have installed our NextJS app, we need to connect our MongoDB database to Buildable. To do this, we can either connect an existing MongoDB Atlas Instance or spin up a new MongoDB Atlas instance (coming soon). For now, we'll be connecting an existing instance.

In your Buildable app, navigate to Databases and click on New. Pick MongoDB as the datasource and select Connect Existing.

Add in your credentials and connect.

Connecting MongoDB

💡 If you get stuck on this step, go ahead to check our docs for more details

Thanks to Buildable, we can create a new collection directly in our IDE. Click MongoDB and create a new collection. Let’s call it blogs.

Create new collection

Step 3: Defining the blog post schema

With the database connected with Buildable, it is time to create the schema for a single blog post. We'll create a basic schema that contains the following:

  • title
  • image
  • tags
  • content
  • author
{
    _id: '1',
    title: 'How to write a great blog post',
    image:
      'https://images.unsplash.com/photo-1524492449090-a4e289316d9c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1694&q=80',
    tags: ["community", "webdev"],
        content: "Lorem ipsum is a pseudo-Latin text used in web design, typography, layout, and printing in place of English to emphasize design elements over content. It's also called placeholder (or filler) text."
    author: "Esther Agbaje"
  },
Enter fullscreen mode Exit fullscreen mode

Step 4: Creating the POST endpoint for new blog posts

Now that we have completed the schema of our blog posts, we need API endpoints to insert new posts into our database. So, let's create a POST endpoint to achieve that.

In the Buildable IDE, click REST and create a new folder called blog.

Next, to create an endpoint, click Create new flow and give it a name, new-post.

Image description

💡 It's important to note that the folder structure or naming affects the URL. We'll be organizing all endpoints in this "blog" folder.

You'd notice this workflow provides a POST endpoint to create new posts and send the data to our MongoDB Atlas.

Post endpoint

Next, we need a way to insert data via the API into our MongoDB database. Buildable provides a list of action templates for MongoDB.

Click Add action, and select MongoDB from the list. Now choose the Insert Document template.

Add action

Navigate to the Body tab and put some dummy data in the request body as follows:

{
  "title": "Testing new blog",
  "image": "https://images.unsplash.com/photo-1661956602944-249bcd04b63f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80",
  "tag": "community",
  "content": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source.",
  "author": "Esther Agbaje"
}
Enter fullscreen mode Exit fullscreen mode

Next, go to the Actions tab. In the action templates, we need to make a couple of edits to the code.

Remember, the collection is called blogs so we adjust this.

💡 $body represents the entire payload data.

To access the key values dynamically, we use

        title: $body.title,
    tag: $body.tag,
    image: $body.image,
    content: $body.content,
    author: $body.author
Enter fullscreen mode Exit fullscreen mode

Now, take out unused code and save the workflow.

Lastly, in the Response tab, we need to modify the response on success. Copy the action and paste into the response. Save changes in the response tab.

Actions for post

Now, run this workflow. Excellent!

Grab the POST endpoint, as we'd be using shortly in our NextJS app.

Step 5: Building out the User Interface (UI) to create a new blog post

A blog isn't very useful without posts. We need to be able to add new posts via the API we just created.

Back in our Next JS app, in the pages directory, create a new page called create-post.jsx.

Here's what we're working towards

Add post

From our schema, we need to be able to send the following fields from a form:

  • Title
  • Image URL
  • Content
  • Tag
  • Author

Here's the snippet of the code for you to easily follow along:

import {
  Box,
  Button,
  Center,
  Container,
  FormControl,
  FormLabel,
  Heading,
  HStack,
  Input,
  Select,
  Stack,
  Text,
  Textarea,
} from '@chakra-ui/react';

import React from 'react';

const CreatePost = () => {
  return (
    <Box>
      <Stack spacing='2' p='4' bg='black' color='white' textAlign='center'>
        <Heading>Create a New Blog post</Heading>
        <Text>Fill in the details to create a new blog post</Text>
      </Stack>
      <Box bg='gray.50' h='100vh'>
        <Container
          bg='white'
          maxW='2xl'
          py='20'
          px='10'
          borderRadius={{ base: 'none', sm: 'xl' }}
        >
          <form>
            <Stack spacing='6'>
              <FormControl>
                <HStack>
                  <FormLabel htmlFor='title'>Title</FormLabel>
                  <Input boxShadow='sm' id='title' type='text' name='title' />
                </HStack>
              </FormControl>
              <FormControl>
                <HStack>
                  <FormLabel htmlFor='image'>Image URL</FormLabel>
                  <Input boxShadow='sm' id='image' type='text' name='image' />
                </HStack>
              </FormControl>
              <FormControl boxShadow='sm'>
                <FormLabel htmlFor='content'>Content</FormLabel>
                <Textarea id='content' name='content' />
              </FormControl>
              <FormControl boxShadow='sm'>
                <FormLabel htmlFor='tag'>Choose tag</FormLabel>
                <Select name='tag' id='tag'>
                  <option value='community'>Community</option>
                  <option value='web-development'>Web Development</option>
                  <option value='automation'>Automation</option>
                </Select>
              </FormControl>
              <FormControl boxShadow='sm'>
                <FormLabel htmlFor='author'>Author</FormLabel>
                <Select name='author' id='author'>
                  <option value='Esther Agbaje'>Esther Agbaje</option>
                  <option value='Tom Jones'>Tom Jones</option>
                  <option value='Sara Nze'>Sara Nze</option>
                </Select>
              </FormControl>
            </Stack>
            <Center>
              <Button
                boxShadow='sm'
                size='lg'
                mt='12'
                colorScheme='blue'
                type='submit'
              >
                Create post
              </Button>
            </Center>
          </form>
        </Container>
      </Box>
    </Box>
  );
};

export default CreatePost;
Enter fullscreen mode Exit fullscreen mode

Now, we need to be able to post data via our API to our database.

We create a function called handleSubmit to post our data.

const handleSubmit = () => {
    const formEl = e.currentTarget;
    e.preventDefault();
 toast({
      title: 'New Post created.',
      status: 'success',
      duration: 9000,
      isClosable: true,
    });
    const formData = new FormData(formEl);
    const data = Object.fromEntries(formData);
    fetch(
      'INSERT POST ENDPOINT HERE',
      {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
          'Content-type': 'application/json; charset=UTF-8',
        },
      }
    )
      .then((res) => {
        res.json();
        formEl?.reset();
      })
      .catch((err) => {
        console.log(err.message);
      });
  };
Enter fullscreen mode Exit fullscreen mode

Remember to switch out 'INSERT POST ENDPOINT HERE' with your actual POST endpoint.

Now, we need to connect the handleSubmit function to the onSubmit event in our form by adding onSubmit={handleSubmit}

Finally, create a post and see what happens. Your post is successfully created, but of course, you don't see it. Why? We can only create new posts with the POST endpoint.

We need to set up a GET endpoint that fetches all the posts. Let's do that in the next step.

Step 6: Creating a GET endpoint to fetch all blog posts

Go to the Buildable IDE and create a new flow within the blog folder. We'll call it get-posts.

Set the flow method to GET.

Image description

Click Add action button. You should see a list of templates. First select MongoDB as the platform and choose the List Data template.

Image description

Now, let's edit the template accordingly. The collection is blogs, and we need the following fields:

  • title
  • image
  • content
  • tag
  • author

List Data

With that completed, copy the actions, navigate to the response tab and paste it in. Save the changes in the response tab.

Add action to node

Next, save the entire workflow and run it. Now copy the GET endpoint. We'd be using it in our NextJS app.

Step7: Building the UI that shows all blogs

Back in our NextJS app, open up index.jsx located in the pages directory. Let's clean it up and add the components we need.

import { Box, Heading, Input, InputGroup, Stack, Text } from '@chakra-ui/react';

const Home = () => {
  return (
    <Box>
      <Stack
        bg='black'
        color='white'
        spacing={{ base: '8', md: '10' }}
        align='center'
        p='8'
      >
        <Stack spacing={{ base: '4', md: '6' }} textAlign='center'>
          <Stack spacing='4'>
            <Text
              fontWeight='semibold'
              color='blue.50'
              fontSize={{ base: 'sm', md: 'md' }}
            >
              Our Blog
            </Text>
            <Heading size='xl'>Latest blog posts</Heading>
          </Stack>
        </Stack>
        <InputGroup size='lg' maxW={{ md: 'sm' }}>
          <Input placeholder='Search' variant='filled' colorScheme='blue' />
        </InputGroup>
      </Stack>
    </Box>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Now, let's fetch the blogposts from the database using NextJS' GetStaticProps.

export async function getStaticProps() {
  const res = await fetch(
    'INSERT GET ENDPOINT HERE'
  );
  const result = await res.json();
  const posts = result.rows;

  return {
    props: {
      posts,
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Remember to switch out 'INSERT GET ENDPOINT HERE' with your actual POST endpoint.

P.S I'm getting the image URLs from unsplash.com, so we need to add the domain in the next.config.js file.

Your next.config.js should look like this:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  images: {
    domains: ['images.unsplash.com'],
  },
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

Finally, let's render the fetched post in the UI, the final code should be

import {
  Badge,
  Box,
  Container,
  Heading,
  HStack,
  Input,
  InputGroup,
  Link,
  SimpleGrid,
  Stack,
  Text,
} from '@chakra-ui/react';
import Image from 'next/image';

const Home = ({ posts }) => {
  return (
    <Box>
      <Stack
        bg='black'
        color='white'
        spacing={{ base: '8', md: '10' }}
        align='center'
        p='8'
      >
        <Stack spacing={{ base: '4', md: '6' }} textAlign='center'>
          <Stack spacing='4'>
            <Text
              fontWeight='semibold'
              color='blue.50'
              fontSize={{ base: 'sm', md: 'md' }}
            >
              Our Blog
            </Text>
            <Heading size='xl'>Latest blog posts</Heading>
          </Stack>
        </Stack>
        <InputGroup size='lg' maxW={{ md: 'sm' }}>
          <Input placeholder='Search' variant='filled' colorScheme='blue' />
        </InputGroup>
      </Stack>

      <Box bg='gray.50' minH='900px'>
        <Container pt='20' maxW='6xl'>
          <SimpleGrid spacing={12} margin='auto' columns={3} pb='8'>
            {posts.map((post) => (
              <Link key={post._id} _hover={{ textDecor: 'none' }} role='group'>
                <Stack boxShadow='base'>
                  {post.image && (
                    <Box overflow='hidden'>
                      <Image
                        src={post.image}
                        alt={post.title}
                        width={400}
                        height={300}
                        objectFit='cover'
                      />
                    </Box>
                  )}
                  <Stack spacing='2' p='6'>
                    <Heading size='md'>{post.title}</Heading>
                    <Text noOfLines={3}>{post.content}</Text>

                    <HStack spacing='8'>
                      <Text as='i' fontSize='sm'>
                        by {post.author}
                      </Text>

                      <Badge colorScheme='orange'>{post.tag}</Badge>
                    </HStack>
                  </Stack>
                </Stack>
              </Link>
            ))}
          </SimpleGrid>
        </Container>
      </Box>
    </Box>
  );
};

export async function getStaticProps() {
  const res = await fetch(
    'INSERT GET ENDPOINT HERE'
  );
  const result = await res.json();
  const posts = result.rows;

  return {
    props: {
      posts,
    },
  };
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Now, go over to localhost:3000/create-post page to create a couple more posts.

Check your homepage, and you should be able to see all the posts you created.

Next Steps

In this tutorial, we built the basic blocks of a blog application - creating new posts and getting all posts. We set up a NextJS app, connected our MongoDB Atlas database in Buildable, and created our API endpoints.

We also learned how to consume our POST API and display data on the frontend. Feel free to add more enhancements to this application, like the ability to edit and delete posts.

If you have any questions, comment below or reach out to us via our Discord Community.

Till next time, keep building with Buildable!

Top comments (1)

Collapse
 
alfirus profile image
alfirus

buildable can't login and main page 404.