DEV Community

Cover image for Tutorial: Build a contact form with NextJS and BCMS
Momcilo
Momcilo

Posted on

Tutorial: Build a contact form with NextJS and BCMS

Forms are critical to practically everything we need to perform in the web development ecosystem. Are you registering for the new software? Do you intend to purchase the new course? You complete a form.

One of the most typical applications of forms is to create a contact page on a website, a form where visitors may send you a message so you can have a real interaction outside of your website, to close a transaction or do something else.

In this tutorial, I will utilize Next.js to develop a simple job form interface and BCMS as a headless CMS to collect data users' input in real-time.

BCMS is a headless Content Management System that allows easy content modeling for creative Next.js applications.

Unlike most Headless CMSs, BCMS allows you to upload content from your frontend application to BCMS, which now serves as a database. In this article, I'll combine the capabilities of Next.js with BCMS to accomplish precisely that.

Prerequisite

To make the most of this tutorial,

You should be acquainted with Next.js.

You should also have a BCMS account; to create one, go to https://thebcms.com.

Nodejs, as well as npm or yarn, should be installed on your machine.

Configuring BCMS

When it comes to using BCMS, you have two possibilities. You can use it either on the cloud or locally (https://docs.thebcms.com/install/locally#install-bcms-cli).

Using BCMS on the cloud simplifies your tasks. It handles server setup and maintenance, as well as ensuring continuous uptime, allowing you to concentrate on front-end development. The API URL is the same from development to production.

Using it locally or self-hosted implies that you run the program locally on your machine and then host it on a server of your choice, such as DigitalOcean.

Now, let's dive right in and explore how you can host on the cloud.

Hosting on the cloud

To host it on the cloud, you can log in to your bcms dashboard and then create an instance on the dashboard.

An instance is essentially a container that stores all the specific application data. When you initially log in, your dashboard will resemble this, except the instances tab might be empty if you haven't created an instance before.

To create a new instance, click Create new instance.

Image description

On the following page, you will be brought to a phase where you can select self-hosted or cloud. Select cloud and begin your free trial.

BCMS Self-Hosted

Simply follow this guide to host BCMS yourself.** If everything is in order, your bcms application should be running at localhost:8080

Modeling BCMS Data

Modeling data in a headless CMS involves defining the structure and organization of your content items, such as articles, products, or job listings. This is accomplished by specifying the types of information each content item can include, such as title, description, image, and name.

Using a flexible data model simplifies this process, enabling developers to retrieve and present the content on different platforms and devices through API calls. This approach eliminates the need for developers to be constrained by a specific frontend design.

Now, with BCMS, two features enable all of this.

Templates In BCMS, templates create content structure by defining the information that each content can have. You can construct numerous or single entries based on that structure.

Entries Each entry in BCMS represents a single Template record.

Now is the time to plan out how to model data or develop a contact form template.

Image description

The first things you'll need are seen in the figure above.

Name - String - Required

Email - String - Required

Message - Rich Text - Required

Creating the template

Now, go to your BCMS and create a new template called "User Message" with an optional description to help you remember the purpose of that template in the future."

Image description

The following step is to include the properties specified above. At this time, your template should look somewhat like this.

Image description

Creating API Keys

API keys are used to link your app to CMS data. Each key allows you to manage permissions for templates, plugins, and functions.

To generate a key, go to the key manager and select Add new key. Fill in the name and optional description in the following step.

Following that, Toggle the permissions for that key by clicking on it. The only thing we'll be doing for this application is adding resources; thus, if you toggle on add resources, everything will be great. However, I will turn everything on from my end.

Image description

Creating the Next.js application

Refer to this documentation to create a Next.Js project.
If the command fails saying Not Logged inโ€ฆ

Image description

Run this command bcms --login and login through the browser window that is automatically opened, it will connect it to your account, and with that you can run bcms --website --create successfully.

Image description

Creating the Contact Page

Copy the pages folder from Github, run npm install, and then click on the contact link on the homepage, which will now be live at localhost:3000

Posting Data to BCMS Using Functions

BCMS Functions are JavaScript functions that can be invoked by making an HTTP request to the back end API. When you create a function, it becomes available at

`POST: https://{CMS_DOMAIN}/api/function/{FUNCTION_NAME}`\
Enter fullscreen mode Exit fullscreen mode

BCMS Functions are used to enhance BCMS capabilities, such as adding an entry or template straight from our frontend. In our scenario, it will be used to generate a new entry based on user input. Because of the expansion of the functions that BCMS provides, it outperforms all other CMS platforms.

Creating the Function

Now, in your BCMS instance folder, navigate to src/functions and create a.typescript file that will act as the function's URL. Then, follow these steps to develop a function that validates the data it gets and transmits it to BCMS.

1. Validating function data

From our template, we can see that weโ€™re expecting a Name, Email, and message fields. so these have to be passed on from the frontend, in other words, they need to be included in the request body that the function will handle.

Image description

Now, I'll develop a validator that expects the Name, Email, and message, and I'll use the Name to auto-generate the slug and title.

First, I'll specify the data I'll obtain from the request body and their types in an interface called Body.

interfaceBody {
    name:string,
    email:string,
    message:string
}

Enter fullscreen mode Exit fullscreen mode

Now I will design the validation model for the request body. I'll use ObjectUtility, which is included in BCMS, to accomplish this. ObjectUtility is comparable to Joi, and you can completely utilize Joi if you like.


import type {ObjectSchema} from "@becomes/purple-cheetah/types";

constBodySchema:ObjectSchema= {
    name: {
        __type: "string",
        __required:true
},

    email: {
        __type: "string",
        __required:true
},

    message: {
        __type: "string",
        __required:true
}
}

Enter fullscreen mode Exit fullscreen mode

Now, Iโ€™ll check the request body to see if it matches to mine expectations.

To accomplish this, I'll now call the createbcmsfunction method.

import {createBcmsFunction} from "@becomes/cms-backend/src/function";
import {HTTPStatus, ObjectUtilityError} from "@becomes/purple-cheetah/types";
import {ObjectUtility, StringUtility} from "@becomes/purple-cheetah";

export defaultcreateBcmsFunction(async() => {
return{
       config: {
           name: 'contact-message',
public:true
},

asynchandler({request, errorHandler}) {
constbody:Body= request.body;
constcheckBody = ObjectUtility.compareWithSchema(body, BodySchema, 'body');
if(checkBodyinstanceofObjectUtilityError) {
throwerrorHandler.occurred(HTTPStatus.BAD_REQUEST, checkBody.message)
           }
       }

   }
});
Enter fullscreen mode Exit fullscreen mode

Save the file and send a request to localhost:8080/api/contact-message

It will return an error if you submit it an empty request. You provide the expected body data, and it returns true.

Image description

2. Making a new entry using body data

So, at this point, you must construct the BCMS entry structure (an array), match their values to the recently validated Body Data, and then send it to bcms.

Now, I will make a new variable named entry, which will be used to store the entry in a BCMS entry. Make a handler.

const entry = BCMSFactory.entry.create({
               templateId: '653a4e79ce69ebb11b9133cc',
               content: [],
               meta: [
                   {
                       lng: 'en',
                       props: [

                       ]
                   }
               ]
           })
//Replace template ID With your template ID.
Enter fullscreen mode Exit fullscreen mode

And for content, I'll use an object like the following:

content: [{
    lng: 'en',
    nodes: [],
    plainText: ''
}],
Enter fullscreen mode Exit fullscreen mode

Following that, the props field in the meta defines the properties that will be supplied as well as their values. To accomplish this, I will manually create a new entry in the bcms dashboard and copy the property definitions and ids from there.

All I have to do now is to replace the data with data from the request body, and also map out the type for the rich text field.

import type { BCMSPropValueRichTextData } from '@becomes/cms-backend/src/types';

const entry = BCMSFactory.entry.create({
    templateId: '653a4e79ce69ebb11b9133cc',
    content: [{
    lng: 'en',
    nodes: [],
    plainText: ''
    }],
    meta: [
        {
            lng: 'en',
            props: [
                {
                    "id": "4b985458-f97d-4c0d-81b9-a526d5b92d2a",
                    "data": [
                        body.name
                    ]
                },
                {
                    "id": "b6433850-3361-4cd3-8d5a-e8120786146a",
                    "data": [
                        StringUtility.toSlug(body.name)
                    ]
                },
                {
                    "id": "a7ddb6bd-8580-4596-8600-1bfd91f37f5c",
                    "data": [
                        body.name
                    ]
                },
                {
                    "id": "23be5efc-cdd4-41d9-ba79-4550a2dfa4ad",
                    "data": [
                        body.email
                    ]
                },
                {
                    "id": "185d7cbe-1134-4eba-88cc-70a3036ec176",
                    "data": [
                        {
                            "nodes": [
                                {
                                    "type": "paragraph",
                                    "content": [
                                        {
                                            "type": "text",
                                            "text": body.message
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
})
Enter fullscreen mode Exit fullscreen mode

3. Posting The entry Data to BCMS

Now I can send data to the BCMS from within the handler function.

//Import this at the top of your code outside the handler function 
import {BCMSRepo} from "@becomes/cms-backend/src/repo";

await BCMSRepo.entry.methods.add(entry);
return{ message: "Success" }
Enter fullscreen mode Exit fullscreen mode

And then test on Postman and check bcms Entries.

Image description

Image description

Posting Data to The function from Next.js Frontend

On the fronted, you can now proceed to make the functionality that calls the bcms function API.

On the contact page, Iโ€™ll add a handleFormSubmit function handler.

import { useState } from 'react';

const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

  const handleFormSubmit = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    const { name, email, message } = formData;
    if (!name || !email || !message) {
      alert('All Fields Are Required.');
      return; // Prevent further execution
    }

    const response = await fetch(
      'http://localhost:8080/api/function/contact-message',
      {
        method: 'POST',
        body: JSON.stringify({ name, email, message }),
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );

    if (response.status === 200) {
      alert('Message sent. You rock!');
       // Reset the form data
       setFormData({ name: '', email: '', message: '' });
    } else {
      // Handle other cases (e.g., server errors) here.
      console.log("We've got some errors.");
    }
  };
Enter fullscreen mode Exit fullscreen mode

And now I can bind the submit button to this submit handler.

<button
                      className="text-white text-base font-normal w-full leading-none"
                      type="submit"
                      onClick={handleFormSubmit}
                    >
                      Submit
 </button>
Enter fullscreen mode Exit fullscreen mode

And then on the different input fields I have, I can bind their reactive value to the value I was sending.

<textarea
                      id="message"
                      className=" block p-2.5 mt-4 w-full text-gray-900 text-xs lg:text-base font-normal rounded-3xl border  border-stone-300 placeholder-black"
                      rows={4}
                      placeholder="Your message..."
                      value={formData.message}
                      onChange={(e) =>
                        setFormData({ ...formData, message: e.target.value })
                      }
></textarea>
// Do this for the different Input fields.
Enter fullscreen mode Exit fullscreen mode

It is time to test on the front end and to get a successful response.

Click on this GitHub to view the finished code for the Next.js Frontend.

Image description

Image description

Conclusion

In summary, handling form submissions effectively is essential for data integrity, user feedback, error handling, data processing, and automation. It ensures a smooth user experience and enables the website or application to function as intended.

By combining the powerful features of Next.js with the content management capabilities of BCMS, you can create dynamic and scalable applications with ease. Whether it's building interactive forms, building a blog, or NextJS e-commerce website, Next.js and BCMS provide a solid foundation for your projects. The Possibilities are endless.

Keep exploring and experimenting with these technologies to discover new possibilities and enhance your development skills. Happy coding!

Top comments (0)