Nextjs Contact Form with App Router and MongoDB

Updated at: 29 Feb 2024Deepak Painkra
 

nextjs contact form

 

In this article, we create a NextJS contact form with an App router, and we will be making an API route to submit our form in MongoDB, so let's get into the tutorial.

 

MongoDB installation & Server Start

First, we need to install the MongoDB community edition and follow this Link to download the community edition. It will also work with the MongoDB atlas.

 

manually turning on MongoDB server on a Windows PC

Replace a URL with the Atlas URL, but when you install MongoDB compass on your system, select as a service if you have more than 8GB RAM, and if you do not want to install as a service, then go on search of Windows, then type Services and open it, after that search MongoDB and turn of the server.

 

One package we need to install is Mongoose to connect with the database. Now, go to the terminal and issue this command,

npm install mongoose

After that, click Connect, and that's it. Now, we have completed the MongoDB compass setup.

 

Environmental Variable Setup

Now, create a .env file in the root directory, which will be outside of the app router, and paste this URL, which is for Mongodb compass,

MONGODB_URL=mongodb://127.0.0.1:27017/

in case you have an atlas URL, that will also work.

 

Creating Models for Contact form

Now, create a folder named models in the root directory, and create a file named Contact.js inside the models folder,

Now, let's create a schema. Here, we need to write a type script, which is very basic,

import mongoose from "mongoose";


const ContactSchema = new mongoose.Schema({
  username: {type: String, required:true},
  email: {type: String, required:true},
  message: {type: String, required:true},
  
}, {
  timestamps: true,
});

export default mongoose.models.Contact || mongoose.model("Contact", ContactSchema);

A timestamp is True, so it will automatically add Created at and Updated at, so we do not need to write them.

Now, let's export these models. The logic of the last line is it will create Contact if it is not available in the database,

 

Connecting with DataBase

Now, create a middleware folder in the root directory and create mongoose.js inside the middleware folder.

Now, we need to import Mongoose and define our environment variable to establish a connection with a database, and this code says if the Mongo_URL is not specified, it will throw an error.



import mongoose from "mongoose"

const MONGODB_URL = process.env.MONGODB_URL;

if (!MONGODB_URL) {
    throw new Error(
        "Please define the MONGODB_URI environment variable inside .env.local"
    )
}


let cached = global.mongoose;

if (!cached) {
    cached = global.mongoose = {con: null, promise: null}
}

const connectDb = async () => {
    if (cached.conn) {
        return cached.conn;
    }


    if (!cached.promise) {
        const opts = {
            bufferCommands : false
        };

        cached.promise = mongoose.connect(MONGODB_URL, opts).then((mongoose) => {
            return mongoose
        })
    }

    try {
        cached.conn = await cached.promise;
    } catch (e) {
        cached.promise = null;
        throw e;
    }

    return cached.conn;
}

export default connectDb;

If a connection does not exist, we check if a promise is already in progress. If a promise is already in progress, we wait for it to resolve to get the connection.

Write the entire code inside the mongoose.js and export the connectDb,

 

Creating an API Route

Now, we need to create an api folder inside the app folder. Now, create a contact folder inside the api folder.

After that, we need to create a route.js inside the contact folder, and the location will be,

app>api>route.js

Inside the router.js file, we need to import middleware, models and NextResponse,

 

We are using async to wait for the connection. After that, we will create a body to get JSON responses, and we will create a connection with the database,

import Contact from "../../../models/contact";
import connectDb from "../../../middleware/mongoose";
import {NextResponse} from "next/server";

export async function POST(req, _res) {
    try {

        const body = await req.json();
        connectDb();

        await Contact.create(body);

        return NextResponse.json({
            message:"Message sent successfully!"
        }, {
            status: 200
        })

    }catch (e) {
        return NextResponse.json(
            { message: "Server error, please try again!" },
            { status: 500 }
        )
    }
}


let cached = global.mongoose;

if (!cached) {
    cached = global.mongoose = {con: null, promise: null}
}

const connectDb = async () => {
    if (cached.conn) {
        return cached.conn;
    }


    if (!cached.promise) {
        const opts = {
            bufferCommands : false
        };

        cached.promise = mongoose.connect(MONGODB_URL, opts).then((mongoose) => {
            return mongoose
        })
    }

    try {
        cached.conn = await cached.promise;
    } catch (e) {
        cached.promise = null;
        throw e;
    }

    return cached.conn;
}

export default connectDb;

After that, it will create a body inside the database, then it will return the response, and if we get an error, it will throw a status 500, which is a bad request.

 

Creating Contact Form

Now, create a contact folder inside the app, also create contact.module.css and page.js inside the contact folder,

Write this code inside the page.js,

'use client'
import styles from './Contact.module.css';
import React, { useState } from 'react';


const Contact = () => {
  const [user, setUser] = useState({
    username: "",
    email: "",
    message: ""
  })
  const [status, setStatus] = useState(null);


  function handleChange(e) {
    const name = e.target.name;
    const value = e.target.value;

    setUser((prevUser) => ({ ...prevUser, [name]: value }));
  }

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      const response = await fetch('/api/contact', {
        method: 'POST',
        headers: { "Content_Type": "application/json" },
        body: JSON.stringify({
          username: user.username,
          email: user.email,
          message: user.message
        })
      })
      
      if (response.status === 200) {
        setUser({
          username: "",
          email: "",
          message: ""
        })
        setStatus('success');
      } else {
        setStatus('error');
      }

    } catch (e) {
      console.log(e)
    }

  }


  return (
    <div className={styles.contactContainer}>
      <div className={styles.contactForm}>
        <h2 className={styles.contactFormTitle}>Contact Us</h2>
        <form onSubmit={handleSubmit}>
          <div className={styles.contactFormField}>
            <label className={styles.contactFormLabel} htmlFor="username">Name:</label>
            <input type="text" id="username" name="username" className={styles.contactFormInput} value={user.username} onChange={handleChange} required />
          </div>
          <div className={styles.contactFormField}>
            <label className={styles.contactFormLabel} htmlFor="email">Email:</label>
            <input type="email" id="emailHelp" name="email" className={styles.contactFormInput} value={user.email} onChange={handleChange} required />
          </div>
          <div className={styles.contactFormField}>
            <label className={styles.contactFormLabel} htmlFor="message">Message:</label>
            <textarea id="message" className={styles.contactFormTextarea} value={user.message} onChange={handleChange} name='message' ></textarea>
          </div>
          <div>
            {status === 'success' && <p className={styles.success_msg}>Thank you for your message!</p>}
            {status === 'error' && <p className={styles.error_msg}>There was an error submitting your message. Please try again.</p>}

            <button type="submit" className={styles.contactFormButton}>Submit</button>

          </div>
        </form>
      </div>
    </div>
  );
}


export default Contact;

Now, write this code inside the contact.module.css,

.contactContainer {
    max-width: 1200px;
    margin: 0 auto;
    padding: 100px 0;
  }
  
  .contactForm {
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    padding: 20px;
  }
  
  .contactFormTitle {
    font-size: 24px;
    margin-bottom: 20px;
  }
  
  .contactFormField {
    margin-bottom: 20px;
  }
  
  .contactFormLabel {
    display: block;
    font-size: 16px;
    margin-bottom: 5px;
  }
  
  .contactFormInput {
    width: 100%;
    padding: 10px;
    font-size: 16px;
  }
  
  .contactFormTextarea {
    width: 100%;
    height: 100px;
    padding: 10px;
    font-size: 16px;
  }
  
  .contactFormButton {
    display: inline-block;
    background-color: #000;
    color: #fff;
    font-size: 14px;
    padding: 8px 16px;
    text-decoration: none;
    border-radius: 4px;
    transition: background-color 0.3s;
  }
  
  .contactFormButton:hover {
    background-color: #666;
  }

  .success_msg{
    color: #a8e48a;
}

.error_msg{
    color: red;
}

I haven't explained this in detail because my JSON object won't allow me to write more.