Search

Suggested keywords:
  • Java
  • Docker
  • Git
  • React
  • NextJs
  • Spring boot
  • Laravel

Data Fetching and Form Building using NextJS

  • Share this:
post-title

Next.js is one of the easy-to-learn frameworks for server-side pre-render pages for client-side web applications. How to get started on Next.js and build a sample site is shown in the previous blog

In this blog, we will see how we can fetch data from API and make it pre-render pages. Also, let's see how forms work in nextJs and collect the data without maintaining the database. This will be a continuation of the consulting template website created in the previous blog where the company wants to project the open-source code contribution and user interface to collect the data from the site visitors. 

Pre-rendering:

Next.js pre-renders every page which helps in performance and SEO. Pre-renders means, it creates the HTML page on the server-side and you can see that page itself coming in the network tab as shown below:

We have two types of pre-rendering, it depends on when it will be generated. If the page is generated at the build time, it is called static generation and if it is generated at the time of request it is called server-side rendering. For our use case of showing all the public open-source contributions, it needs to be dynamically fetched from Github on each request. The previous blog was an example of static generation as it was all statically generated.     

Fetching GitHub Repos:

Github provides API to fetch the repositories metadata details. It is free-to-use simple APIs, we can look at the documentation for more details. Used fetch javascript API to fetch the data and resolve the data to set that as props for the about page. 

If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps. Github API provides a lot of data but we show a few of the data on the screen. So returning props object with the required fields. 

export async function getServerSideProps(context) {
  const res = await fetch("https://api.github.com/users/devgroves/repos");
  const data = await res.json();
  // console.log('data', data.filter((elem) => !elem.name.includes("tutorial")));
  const filteredData = data.filter(elem => isReadyProject(elem)).map((elem) => {
    return {
      id: elem.id, name: elem.name, html_url: elem.html_url, description: elem.description, full_name: elem.full_name,
      language: elem.language, stargazers_count: elem.stargazers_count, forks: elem.forks
    };
  });
  return {
    props: {
      data: filteredData
    },
  };
}

Also, I need to project the projects which are ready to use and skip the WIP (work in progress) kind of projects. To do that, you can create a function and use it as part of the filter before mapping it. isReadyProject is a function that will filter the project. It checks the topics metadata of each repo which does not contain "wip-do-no-use". This function can be customized to your need by changing to any other GitHub parameters and do it.  

function isReadyProject(elem) {
  let topics = elem.topics;
  let result = true;
  topics.forEach((topicName) => {
    if (topicName.search("wip-do-not-use") >= 0) {
      result = false;
    }
  });
  return result;
}

Now we are going to display a list of cards layout for each repo. I am going to use react-bootstrap to make it responsive and it will be like having a card for a repository so we can loop for each repository. There will be a demo.png file in each repository in which we will show the left part of the card. Also used the react icons package to show the key things like language, stars and forks with icons as react components. 

Filename: pages/about.jsx       

<Row>
       <h3 className="repo-header">OUR Public Code Repositories For Glance</h3>
        </Row>
        <Row>
          {data &&
            data.map((val) => (
              <Col key={val.id} md={6} sm={12}>
                <Card className="repo-card">
                  <Row>
                    <Col md={4} className="projectCard">
                      <Image
                        src={`https://raw.githubusercontent.com/${val.full_name}/main/demo.png` || Image1}
                        alt="image"
                        width={300}
                        height={250}
                      />
                    </Col>
                    <Col md={6}>
                      <Card.Body>
                        <Card.Link href={val.html_url}>{val.name}</Card.Link>
                        <Card.Text className="projectCard-text">{val.description}</Card.Text>
                        <div className="projectCard-body">
                          <p>
                            <GoPrimitiveDot /> {val.language}
                          </p>
                          <p>
                            <GoStar /> Stars:{val.stargazers_count}
                          </p>
                          <p>
                            <GoRepoForked /> Forks:{val.forks}
                          </p>
                        </div>
                      </Card.Body>
                    </Col>
                  </Row>
                </Card>
              </Col>
            ))}
        </Row>

The public code repository gallery will show like below:

Working with forms:

A web form has a client-server relationship. They are used to send data handled by a web server for processing and storage. The form itself is the client, and the server is any storage mechanism that can be used to store, retrieve and send data when needed.

HTML forms are built using the <form> tag. It takes a set of attributes and fields to structure the form for features like text fields, checkboxes, dropdown menus, buttons, radio buttons, etc.

I need a contact form, so customers can do an inquiry by filling it out. It will collect the basic details like firstname, lastname, contact number, mail id, attachment and description. Thanks to W3C community, input types are specifically created for phone, email so it will work intuitively based on the data type. A simple form validation will be sufficient as it has to customer-friendly form to enter their details quickly. Client-side form validation is to check for mandatory fields, validating data types and its limits. Server-side validation is preferable for more secure validation. 

Filename : pages/contact.jsx

<Row className="center container-height zero-margin contactform-bg">
          <Col md={6} sm={12} className="mobile-container">
            <Image
              src={ContactUsImage}
              alt="Contact us image"
              height={400}
              width={600}
              placeholder="blur"
              className="imgContain"
            />
            <Breadcrumbs name="Contact us" />
          </Col>
          <Col>
            <div className="form-container">
              <form onSubmit={handleSubmit}>
                <h5>Please fill in the form, we will get back to you ... </h5>
                <Row>
                  <Col>
                    <input
                      type="text"
                      id="name"
                      name="firstname"
                      onChange={(e) => {
                        setName(e.target.value);
                      }}
                      placeholder="Your name please.."
                      height="1"
                      required
                    />
                  </Col>
                </Row>

                <Row>
                  <Col md={6} sm={12}>
                    <input
                      type="tel"
                      id="number"
                      name="number"
                      placeholder="Contact number"
                      required
                      onChange={(e) => {
                        setNumber(e.target.value);
                      }}
                    />
                  </Col>
                  <Col md={6} sm={12}>
                    <input
                      type="email"
                      id="email"
                      name="email"
                      placeholder="Email Id.."
                      required
                      onChange={(e) => {
                        setEmail(e.target.value);
                      }}
                    />
                  </Col>
                </Row>

                <Row className="formItem">
                  <Col>
                    <input
                      type="file"
                      id="file"
                      name="file"
                      placeholder="Attach Your file"
                      onChange={(e) => {
                        setFile(e.target.value);
                      }}
                    />
                  </Col>
                </Row>
                <Row>
                  <Col>
                    <textarea
                      id="subject"
                      name="subject"
                      placeholder="Please drop in your questions.."
                      required
                      onChange={(e) => {
                        setMessage(e.target.value);
                      }}
                    ></textarea>
                  </Col>
                </Row>
                <Row>
                  <Col>
                    <input type="submit" value="Submit" />
                  </Col>
                </Row>
              </form>
            </div>
          </Col>
          <p className="aligncenter">
               { showSuccess? "We noted the details, thanks for your time!": "" }
          </p>
        </Row>

On submission of the form, handleSubmit javascript function will be invoked on the client-side.  Now it collects form data as json and hit the backend API api/contact. 

const handleSubmit =async (event) => {
    event.preventDefault();
    const data = {
      "name":name,
      "email":email,
      "number":number,
      "file":file,
      "message":message
    }
    const res = await fetch("/api/contact", {
      method: "post",
      body: JSON.stringify(data),
    });
    const result = await res.json();
    setShowSuccess(true);
    setTimeout(()=> setShowSuccess(false), 2000);
    console.log("handle submit ", result);
   }

Now in the project structure, under the pages/api folder, we can create a contact API (/contact.js). In this file, we create a handler with request and response HTTP objects. Now in the request, you will receive the form data. Instead of setting up a database, collect the information and send it to the official email id. nodemailer is a simple-to-use package for sending emails. Use transporter.sendMail to send the mail with form data which returns a promise to be checked for success or failure. 

export default function handler(req, res) {
  const body = JSON.parse(req.body);
  console.log(body.message);
  const transporter = nodemailer.createTransport({
    service: "gmail",
    auth: {
      user: "test@example.com",
      pass: "XXXXXX",
    },
  });

  const mailOptions = {
    from: "test@example.com",
    to: "devgrovestechnologies@example.com",
    subject: "Contact form from devgroves",
    text: body.message,
  };

  transporter.sendMail(mailOptions, function (error, info) {
    if (error) {
      console.log(error);
    } else {
      console.log("Email sent: " + info.response);
    }
  });
  
  res.status(200).json({ status: "OK" });
}


Github project link: https://github.com/devgroves/consultingtemplatebynextjs

DevGroves Technologies

About author
DevGroves Technologies is a IT consulting and services start-up company which is predominately to web technologies catering to static website, workflow based CRM websites, e-commerce websites and reporting websites tailoring to the customer needs. We also support open source community by writing blogs about how, why and where it need to be used for.