DEV Community

Cover image for Building a client database of people in React
Caleb O.
Caleb O.

Posted on • Updated on

Building a client database of people in React

Ever wondered how to get access to a whole lot of information about people on the internet? Like, any information about anyone?

as you proceed reading this article, you'll come across a section that talks about protecting your API key on the server. If you want to learn more about how you can do that to your API endpoints, you should take a look at this piece

In this article, we’ll be building a client-side application with ReactJS on the web that’ll integrate Kelvin Data API at the frontend. The Kelvin Data is an API platform that allows you to access the profile of people on the web.

You can search for a specific person, either with their email address, LinkedIn profile (here, you’ll have to provide the person’s LinkedIn URL. i.e. https://linkedin.com/in/example-person ), or their phone numbers.

Now, let’s see how we can create an app that we’ll consume this API with. But, before you read this article any further, you should have a basic knowledge of:

  • React,
  • Data fetching with Hooks in React,
  • Conditional rendering in React,
  • String/template Literals in JavaScript,
  • React components and props

The KelvinData dashboard.

When you signup on to the platform, you’ll have access to a personalized dashboard that you can use to watch your subscriptions, manage your API keys, and do a lot more.

kelvinData dashboard

A search playground is also on the dashboard where you can test the features of the API.

You can decide to search for anyone, either with their names, email addresses, LinkedIn profiles, or phone numbers. This article is going to cover how you can search for people with their full name alone.

Getting Started

For the scope of this article, we’ll be using NextJS to bootstrap our app. This does not mean that the create-react-app library wouldn’t work. You can make use of anyone that you find convenient. We’re using nextjs because of its simplicity. You can read more about NextJS here

Let us start by installing the dependencies that we need in this project. We’d start by creating a nextjs app. The command below does that for us.

npx create-next-app [name-of-your-app]
Enter fullscreen mode Exit fullscreen mode

We’ll make use of the "styled-components" library for styling our app, and the "axios" library to fetch data from the API. We wouldn’t be covering much of the styling aspect in this article. You can find the full app styles here.

Let’s get the dependencies above by typing the command below into our terminal.

npm install axios styled-components react-icons
Enter fullscreen mode Exit fullscreen mode

Let us have a look at the file structure of the app below. We’ll focus on the important files that we need in this app, so it’ll be concise.

|--pages
|   |-- api
|   |   |-- users.js  
|   |-- _app.js
|   |-- index.js
|--src
|   |-- components
|   |     |-- Card.js
|__ .env.local
Enter fullscreen mode Exit fullscreen mode

Overview of the components in our Next.js app

In this section, we are going to see the different files that make up the architecture of this project, and their respective functions below.

The pages directory is where all the routing of the app takes place. This is an out-of-the-box feature of Nextjs. It saves you the stress of hard hard-coding your independent routes.

  • pages/api: the api directory enables you to have a backend for your nextjs app, inside the same codebase, instead of the common way of creating separate repositories for your REST or GraphQL APIs and deploying them on backend hosting platforms like Heroku, and so on.

With the api directory, every file is treated as an API endpoint. If you look at the api folder, you'll notice that we have a file called user.js in it.

That file becomes an endpoint, which means an API call can be performed using the path to the file as the base URL.

const getData = async() => {
  axios.get("/api/users")
   .then(response => response())
   .then(response => console.log(response.data))
   .catch(err => console.log(err)
}
Enter fullscreen mode Exit fullscreen mode
  • pages/_app.js: is where all our components get attached to the DOM. If you take a look at the component structure, you’ll see that all the components are passed as pageProps to the Component props too.

It is like the index.js file when using Create-React-App. The only difference here is that you are not hooking your app to the DOM node called “root”

React.render(document.getElementById("root), <App />)
Enter fullscreen mode Exit fullscreen mode
  • index.js is the default route in the pages folder. That is where we'll be doing most of the work on this project. When you run the command below, it starts up a development server and the contents of index.js are rendered on the web page.
npm run dev
Enter fullscreen mode Exit fullscreen mode
  • Card.js: is the component that holds displays the data we get from the API on the webpage

  • .env.local: is where we’re storing the API key that’ll enable us to consume this API.

Writing the server-side API call.

In the previous section, we saw the files that we'll be interacting with and their specific functions. In this section, we would have a look at how we can consume the API.

The reason why we're writing the API call at the server-side is for securing our API key, and Nextjs already makes it an easy task for us.

With the API routes in Nextjs, we can perform our API calls without the fear of our API keys being revealed on the client-side.

You may have been wondering what the essence of the environment variable in the .env file is, in this scenario.

The environment variable (which is our API key) can only be available in development mode. That is why we can do something like process.env.api_key, and get access to the environment variable.

But, the moment you deploy your app to platforms like netlify or vercel, the mode changes to production, which makes the nodejs process object unavailable on the client-side.

Now that you have seen the reason why need to write a server-side API call. Let's get to it right away.

// users.js
import axios from "axios"

export default async function users(req, res) {
    const {
      query: { firstName, lastName },
    } = req;

    const baseUrl = `https://api.kelvindata.com/rest/v1/search-v2?    lastName=${lastName}&firstName=${firstName}&apiKey=${process.env.KEY}`;
    const response = await axios.get(baseUrl);
    res.status(200).json({
    data: response.data,
  });
}
Enter fullscreen mode Exit fullscreen mode

In the snippet above, we created an asynchronous function called, users. It takes in two arguments, req which stands for “request” in full, and res which is “response” in full.

The req argument has some properties, (or “middlewares” as the Nextjs docs calls it) that can be accessed when we’re consuming our API, one of them is req.query.

You’ll notice that we destructured the query property in the snippet above, so we would be able to pass those variables as values to the query properties of the API endpoint. Take a look at it below.

You can read more about the in-built middlewares that comes with the req argument here.

const {
  query: { firstName, lastName },
} = req;
Enter fullscreen mode Exit fullscreen mode

The base URL takes the destructured query properties as values and the apiKey is gotten from the .env file via the nodejs process object.

The destructured query properties are taken as requests that will be sent from the input values of the form component (which we’ll be creating in the next section) to the API, once it is received, we get a response that corresponds with the request we made.

const baseUrl = `https://api.kelvindata.com/rest/v1/searchv2?  lastName=${lastName}&firstName=${firstName}&apiKey=${process.env.KEY}`;
Enter fullscreen mode Exit fullscreen mode

The next process the function has to complete is the response from the asynchronous API call. The snippet below assigns the API call which we are perfoming with the axios library to a variable, response.

On the next line, the res argument uses the status method which is used to send a JSON response to us, then we can assign the response variable as a property of data

You can read more about the various HTTP status codes here

const response = await axios.get(baseUrl);
res.status(200).json({
  data: response.data,
}); 
Enter fullscreen mode Exit fullscreen mode

Building the form component

In the previous section, we saw how we can write our API calls on the server-side. we’ll be using that API call in this section as we create the form component that will send the first and last name values from the input fields to API query parameters.

We’ll be keeping the code snippets short so that this article doesn’t get too long. Let’s start by taking a look at the content of index.js below.

import React from "react";
import styled from "styled-components";
import axios from "axios";
import Card from "../../components/Card";

const Wrapper = styled.section`
  padding: 0 100px 0 100px;
  height: 100%;
  width: 100%;

  // remaining style goes here
`;

const UserAPIComponent = () => {
const [userData, setUserData] = React.useState([]);
const [firstName, setFirstName] = React.useState("");
const [lastName, setLastName] = React.useState("");

const getuserData = async () => {
  // api call goes here
};

const handleSubmit = (e) => {
   e.preventDefault();
   getuserData();
};

return (
   <Wrapper>
     <h3>Search for Anyone</h3>
     <form onSubmit={handleSubmit}>
        <label htmlFor="firstname">First name</label>
        <input
          type="text"
          name="firstname"
          value={firstName}
          placeholder="First Name"
          onChange={(e) => setFirstName(e.target.value)}
        />
        <label htmlFor="lastname">Lastname</label>
        <input
          type="text"
          name="lastname"
          value={lastName}
          placeholder="Lastname"
          onChange={(e) => setLastName(e.target.value)}
        />
        <div className="btn-container">
           <Button>Search</Button>
        </div>
      </form>
      <div className="results-container">
        {userData ? <Card result={userData} /> 
        : "loading..."}
      </div>
  </Wrapper>
 );
};

export default UserAPIComponent;
Enter fullscreen mode Exit fullscreen mode

Since this is a React component that is receiving data from an API endpoint, it should have an internal state of its own. The snippet below shows how we defined the different state variables with React Hooks.

const [userData, setUserData] = React.useState([]);
const [firstName, setFirstName] = React.useState("");
const [lastName, setLastName] = React.useState("");
Enter fullscreen mode Exit fullscreen mode

The firstName and lastName variables receive the text values that are typed into the input field by anyone.

The userData state variable helps us store the response that we get from the API call in an array, so we can use the JavaScript map() method to render the response on the webpage.

Notice how we’re using axios to get data from the API endpoint, and how the base URL is not a typical https:// URL, instead, it is the path to the file where we made the server-side API call before.

const getuserData = async () => {
axios.get(`/api/usersfirstName=${firstName}&lastName=${lastName}`, {
       headers: {
         Accept: "application/json",
         "Access-Control-Allow-Origin": "*",
       },
})
  .then((response) => response)
  .then((response) => {
    setUserData(response.data.data); 
  })
  .catch((err) => console.log(err));
};
Enter fullscreen mode Exit fullscreen mode

We repeat almost the same process in the user.js file, but this time around with the necessary fetch headers and assignment of the state variables to the API query parameters.

In the second .then() method, we ensured that the response from the API call is treated as an array, hence the need for response.data.data. If we had stopped at setUserData(response.data), JavaScript will throw a type-error whenever we try to do the following:

{
  userData.map((users, index) => {
    return (
      // some JSX
    )
  })
}
Enter fullscreen mode Exit fullscreen mode

This is because response.data is having an object data-type, and the map() operation does not work on JavaScript objects, only arrays.

The handleSubmit handler ensures that the webpage isn’t reloaded at every API call, upon the click of the search button.

const handleSubmit = (e) => {
  e.preventDefault();
  getuserData();
};
Enter fullscreen mode Exit fullscreen mode

Building the card component

The card component serves as the presentational component of the app. Data gets passed down to it through the usage of props in React.

Once again, for the sake of brevity, we’ll not be taking a look at all the major content of the card component. Let’s take a look at the modified structure below

import React from "react";
import { FiUser } from "react-icons/fi";
import styled from "styled-components";

const Wrapper = styled.div`
  height: 56%;
  width: 32%;
  margin: 0 15px 30px 0;
  background: #fff;
  box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.05);
  border-radius: 8px;
  padding: 0 15px 0 15px;
`;

const Card = ({ result }) => {
  return (
     <>
      {result.map((users, index) => {
        return (
           <Wrapper className="users-card" key={index}>
             <p>
               <span>
                 <FiUser />
               </span>
                 {users.name.full !== null ? 
                   users.name.full 
                   : "no name data" }
             </p>
             <p>Title: {users.employments[0].title}</p>
             <p>
               Organization:
                {
              users.employments[0].organization.name !== null
              ? users.employments[0].organization.name
              : "no employment info"}
             </p>
          </Wrapper>
        );
      })}
   </>
  );
};

export default Card;
Enter fullscreen mode Exit fullscreen mode

The result prop is passed to the Card component, which in turn, gets utilized in the App component (in index.js).

The ternary operator checks for the validity of userData, if it is true, the Card component is rendered. If it isn’t, the loading… string is displayed on the webpage.

<div className="results-container">
  {userData ? 
    <Card result={userData} /> 
    : "loading..."
  }
</div>
Enter fullscreen mode Exit fullscreen mode

You’ll also notice how we’re performing conditional rendering with the ternary operator in the “Organization” paragraph below.

If there isn’t any data that corresponds with a user’s organization details, the "no employment info" string is displayed. If there is, the user’s organization is displayed.

<p>
  Organization:
  {users.employments[0].organization.name !== null
  ? users.employments[0].organization.name
  : "no employment info"}
</p>
Enter fullscreen mode Exit fullscreen mode

Conclusion

The video below shows the end result of what we have been building all along from the beginning of this article. You can always check your browser’s dev tools, move to the network tab, to see if the API key is showing or not.

kelvin data demo app

If you want to have a look at the code base, here’s the link to it.

The link points to the specific file (of this article) in the repository. You can check out my other article demos here and also have a look at the repository as a whole.

If you read this article up till this point, thank you for doing so, and please don’t forget to share it.

Top comments (6)

Collapse
 
lioness100 profile image
Lioness100

Great read!

Collapse
 
seven profile image
Caleb O.

Thank you! 😊

Collapse
 
arkadeepnag profile image
Arkadeep Nag

It was amazing .

Collapse
 
seven profile image
Caleb O.

I'm glad you found it helpful @arkadeepnag 🍷

Collapse
 
boaty profile image
Thiraphat-DEV

Nice a Blog

Collapse
 
seven profile image
Caleb O.

Thank you.🍷