DEV Community

Cover image for React & REST API: How to add data to a Web Database
William Sayama
William Sayama

Posted on • Updated on

React & REST API: How to add data to a Web Database

General Overview

In Part 5 of this series, we'll add a form to our React App. Clicking a button will add a new record into our Kintone App, and the latest data will be re-rendered onto our React App.

An image showing the React App before and after the re-render

System Overview

Our React App will use the same Express server used in the previous articles as a proxy. The Express server will take in POST requests from our React App, make a request to our Kintone App (the web database), and add a new record into it.
After that, the React App will make a GET request to the Express server. The Express server will obtain records from our Kintone App, send back the result to the React App, and the result will be re-rendered on the UI.

An image showing the system overview of this project where requests from the React App pass through the Express server to make API requests to Kintone

Update the Server Side Code

We'll update the server.js code we've been working on in the series.

1. Set the Request End-points

We'll be making a REST API call to a new end-point that will add a record into the Kintone App. Since we have 2 end-points now, for better readability let's first update our endpoint from this:

const requestEndpoint = "https://{subdomain}.kintone.com/k/v1/records.json";
Enter fullscreen mode Exit fullscreen mode

to this:

const multipleRecordsEndpoint = "https://{subdomain}.kintone.com/k/v1/records.json";
const singleRecordEndpoint = "https://{subdomain}.kintone.com/k/v1/record.json";
Enter fullscreen mode Exit fullscreen mode

The end-point called in the /getData route should also be updated from requestEndpoint to multipleRecordsEndpoint.

//const response = await fetch(requestEndpoint+parameters, fetchOptions);
const response = await fetch(multipleRecordsEndpoint+parameters, fetchOptions);
Enter fullscreen mode Exit fullscreen mode

2. Add a New Route

We currently have one route, the /getData route, that takes care of getting data from our Kintone App. Let's define another route that will take care of adding data into our Kintone App.

app.post('/postData', cors(corsOptions), async (req, res) => {

});
Enter fullscreen mode Exit fullscreen mode

The same CORS options used in our /getData route will be used in this /postData route. The settings inside this /postData route will be similar to the /getData route in terms of how the REST API is called, except that we'll have to set it to handle a POST request instead of a GET request.

The first thing we'll do in this /postData route is to define the request body needed for Kintone's Add Record API call. The parameters app and record will be needed in this request.

const requestbody = {
    "app":1,
    "record":{
        "title":{
            "value":req.body.title
        },
        "author":{
            "value":req.body.author
        }
    }
};
Enter fullscreen mode Exit fullscreen mode

Make sure to set the App ID that you are using in your own Kintone domain for the app parameter. For the record parameter, we've set the key names as title and author. These are the field code names of the fields we want to populate data with when the record is added. The values for these keys are set as req.body.title and req.body.author, which are the values that will be included in the body of the POST request from the client side (initiated by a button click).

Next, let's declare the fetch options. As stated in Kintone's API documentation, we'll need to set the method as POST and the Content-Type as application/json. The body defined earlier will also be included in the options.

const options = {
    method: 'POST',
    headers:{
        'X-Cybozu-API-Token':process.env.API_TOKEN,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(requestbody)
}
Enter fullscreen mode Exit fullscreen mode

The option is then set in the 2nd argument of the fetch request.

const response = await fetch(singleRecordEndpoint, options);
const jsonResponse = await response.json();
res.json(jsonResponse);
Enter fullscreen mode Exit fullscreen mode

3. Set a Body Parsing Middleware

A body parsing middleware needs to be set on our Express server, so that the request body from the client side can be read successfully. Let's install body-parser through the terminal. --> Sorry! Looks like body-parser was deprecated.

We'll then declare the body parser at the top of our server.js code.

We'll use express.json() to parse our incoming requests.

Set the following line after declaring const app = express();

app.use(express.json());
Enter fullscreen mode Exit fullscreen mode

And with that, our server side should be ready to read bodies of incoming requests. Let's restart the server to apply the changes (ctrl+c --> node server.js).

Update the API Token Settings

The current API Token we use does not have permission to add new records into the Kintone App. Access the Kintone App's API Token settings and update this API Token so that the "Add records" permission is included.

A screenshot of the API Token settings of the Kintone App

Save the API Token settings, and click on the Update App button to have the new settings be applied to the production environment.

At this point, we can test to see if our Express server is working. By navigating to our React App, we can run the following code in the developer console to see if a successful request can be made to the server.

const addRecordEndpoint = "http://localhost:5000/postData";

const options = {
        method: 'POST',
        headers:{
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(
            {
            'title':'Test Title',
            'author':'Test Author'
            }
        )
    }

const response = await fetch(addRecordEndpoint, options);
const jsonResponse = await response.json();
console.log(jsonResponse);
Enter fullscreen mode Exit fullscreen mode

If successful, the REST API call should return a JSON including the Record ID of the newly added record.

Update the Client Side Code

Our next step is to update our client side code, index.js.
We need to:

  • create a form with 2 input fields (one for the Title name, and one for the Author name) and 1 button
  • send data to our Express server when a button is clicked
  • re-render the list with the newest data

1. Set the Request End-points

We'll first need to declare the new end-point /postData defined earlier in our Express server. For better readability of our code, let's also update the constant name given to the /getData end-point.

//const restEndpoint = "http://localhost:5000/getData";
const getRecordsEndpoint = "http://localhost:5000/getData";
const addRecordEndpoint = "http://localhost:5000/postData";
Enter fullscreen mode Exit fullscreen mode

Don't forget to also update the end-point name called in the fetch call of the callRestAPI function from restEndpoint to getRecordsEndpoint.

//const response = await fetch(restEndpoint);
const response = await fetch(getRecordsEndpoint);
Enter fullscreen mode Exit fullscreen mode

2. Use States for the Input Fields

When we click the button in our form, we want to take the values inside our input fields, and send it in our REST API request. These input field values are best stored in states. In our example, we'll be placing two input fields in our form, one for the Title and one for the Author. Inside the RenderResult function, let's define the states that will manage our input values.

const [titleValue, setTitleValue] = useState("");
const [authorValue, setAuthorValue] = useState("");
Enter fullscreen mode Exit fullscreen mode

3. Create a Form

We'll create a form with two input fields and a button that will be used to send data to our Kintone App. This form will be defined in the return statement of the RenderResult function.

return(
    <div>
        <h1>React App</h1>
            <ul>{apiResponse}</ul>
        <form>
            <div>
                <label htmlFor="title-input">Title:</label>
                <input type="text" value={titleValue} id="title-input" onChange={HandleTitleChange} />
            </div>
            <div>
                <label htmlFor="author-input">Author:</label>
                <input type="text" value={authorValue} id="author-input" onChange={HandleAuthorChange} />
            </div>
            <button type="button" onClick={ButtonClick}>Add data</button>

        </form>
    </div>
);
Enter fullscreen mode Exit fullscreen mode

The htmlFor attribute is React's version of the for element, i.e. it associates the label with the input element. The initial input values are set as the data set in the titleValue and authorValue states. The 2 input fields and the button have events which will run functions when they are triggered.

Our React App will currently display some errors in the console, since we haven't defined the titleValue,authorValue and ButtonClick functions yet. Let's quickly set these in the RenderResult function.

function HandleTitleChange(){};
function HandleAuthorChange(){};
function ButtonClick(){};
Enter fullscreen mode Exit fullscreen mode

Our React App should now render a form 😋

A screenshot of the React App with a form under the list of records

As an extra note, we're defining these functions within the RenderResult function, as those functions will be working with some state variables.

3a. Define the onChange functions

OK, so let's go ahead to update the functions called by the onChange event. These functions will set the current input field values in their relative states, titleValue and authorValue.

function HandleTitleChange(event){
    setTitleValue(event.target.value);
}

function HandleAuthorChange(event){
    setAuthorValue(event.target.value);
}
Enter fullscreen mode Exit fullscreen mode

3b. Define the Button Click function

When the button is clicked, we want our React App to:

  • make a REST API call to the Express server
  • re-render the UI to show that the data is being added
  • re-render the UI with the latest data

So first, we'll update our apiResponse state to include a now loading message at the end of its list after the button is clicked. After that, we'll run an AddNewRecord function, passing in the values of the Title and Author input fields as its argument.

Let's update the ButtonClick function.

function ButtonClick(){
    setApiResponse(apiResponse.concat(<li key="0" >*** now loading ***</li>));
    AddNewRecord(titleValue, authorValue);
}
Enter fullscreen mode Exit fullscreen mode

Note that we needed to give the list a key, so we gave it an ID of 0, which is a number that is never assigned to a Record ID of a record in Kintone (more about keys were mentioned in the previous article).

Now let's add the AddNewRecord function outside of the RenderResult function. The AddNewRecord function will be making our REST API call to the /postData end-point.

const AddNewRecord = async (Title, Author) => {

};
Enter fullscreen mode Exit fullscreen mode

Inside this function, we'll first define the body parameters needed for Kintone's Add Record API. Although the documents state that the app parameter is also needed, we've already defined this parameter on the Express server side, so we won't need to include it on the client side.

const recordBodyParameters = {
    'title':Title,
    'author':Author
}
Enter fullscreen mode Exit fullscreen mode

Next, we'll define the options needed for our fetch call to our Express server.

const options = {
    method: 'POST',
    headers: {
        'Content-Type':'application/json'
    },
    body: JSON.stringify(recordBodyParameters)
}
Enter fullscreen mode Exit fullscreen mode

The options will then be set as the 2nd argument of our fetch call to the Express Server.

const response = await fetch(addRecordEndpoint, options);
const jsonResponse = await response.json();
console.log(JSON.stringify(jsonResponse));
return jsonResponse;
Enter fullscreen mode Exit fullscreen mode

So with this, we've defined a function that will send data (of the field values of the two input fields) to the Express server.

The AddNewRecord function should look like this.

const AddNewRecord = async (Title, Author) => {
  const recordBodyParameters = {
    'title': Title,
    'author': Author
  }

  const options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(recordBodyParameters)
  }

  const response = await fetch(addRecordEndpoint, options);
  const jsonResponse = await response.json();
  console.log(JSON.stringify(jsonResponse));
  return jsonResponse;
};
Enter fullscreen mode Exit fullscreen mode

3c. Set the Render Timing

At this stage, we can test out our React App. Go ahead to place some data into the input fields and click the button.

A gif of clicking the button to add a new record into Kintone although the list does not re-render with new data

Although the now loading message is rendered and nothing happens after that, the data is successfully adde to our Kintone App.

A screenshot of the Record list page of Kintone with the new data added

Now that we've confirmed that a record can be added to our Kintone App, let's fix the rendering issue. After adding a new record, another REST API call needs to be made to Kintone to get the latest data, which will be used to re-render our React App.

To do this, we can utilize our existing useEffect hook that already makes a call to get the latest data from our Kintone App. The second argument of this hook is the dependency array, which determines the timing of when the hook should run. Currently this is set as [] which runs the hook only after the initial render. If we set a state instead for this dependency array, the hook will run when that state changes.

Within the RenderResult function, let's define a new state that will take care of when the useEffect hook will run.

const [successCounter, setSuccessCounter] = useState(0);
Enter fullscreen mode Exit fullscreen mode

We'll call the setSuccessCounter function within the ButtonClick function. The successCounter state value will update after a successful REST API call from the AddNewRecord function.

function ButtonClick(titleValue,authorValue){
    setApiResponse(apiResponse.concat(<li key="0" >*** now loading ***</li>));
    AddNewRecord(titleValue, authorValue)
    .then(response => 
        {
            setSuccessCounter(successCounter+1);
        }
    );
};
Enter fullscreen mode Exit fullscreen mode

We need the useEffect hook to run whenever the successCounter state updates. We can do that by setting the successCounter state as the dependency array of the useEffect hook.

useEffect(() => {
    callRestApi().then(
        result => setApiResponse(result));
},[successCounter]);
Enter fullscreen mode Exit fullscreen mode

With this, the useEffect hook will run after a record is added into our Kintone App. The useEffect hook will get the latest records from the Kintone App and update the apiResponse state. The change in the apiResponse state will cause a re-render, so that all of the obtained records (including the newly added record) will be displayed on the React App.

A gif of the React App with the button being clicked and the list of records being re-rendered with new data

Yay, we did it!

The complete code

The complete code was pretty long, so I've summed up both index.js and server.js in this GitHub repo.

Next steps

Why not post a new dev.to article with some updated version of this code? For example, the onChange events and related states can be merged, input fields can be cleared after clicking, and Memoizing can be used for better render performance. There are a whole lot of ways to make this code better 🤩

Let me know if you have any questions, issues or advices in the comments!

                   _人人人人人人人人人人_
                   > HAVE FUN CODING <
                    ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
                   (\__/) 
                   (•ㅅ•) 
                   /つ つ
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
will_yama profile image
William Sayama

Through one of our online workshops, we realized that body-parser was deprecated in this project. We've replaced this by using express.json() instead - this has been noted in this article as well.