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.
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.
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";
to this:
const multipleRecordsEndpoint = "https://{subdomain}.kintone.com/k/v1/records.json";
const singleRecordEndpoint = "https://{subdomain}.kintone.com/k/v1/record.json";
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);
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) => {
});
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
}
}
};
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)
}
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);
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 --> Sorry! Looks like body-parser was deprecated.body-parser
through the terminal.
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());
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.
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);
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";
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);
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("");
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>
);
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(){};
Our React App should now render a form 😋
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);
}
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);
}
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) => {
};
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
}
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)
}
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;
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;
};
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.
Although the now loading message is rendered and nothing happens after that, the data is successfully adde to our Kintone App.
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);
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);
}
);
};
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]);
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.
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 ̄
(\__/)
(•ㅅ•)
/つ つ
Top comments (1)
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.