Simple Spring Boot Service to Kubernetes Application: Step 16
I have not written a front end application in almost 20 years. Lets try something new. I’m going to be starting slow with a ‘Hello World’ application using typescript and node to deploy to our kubernetes cluster.
Setting Up the Environment
I’m still on windows (sigh) and was having trouble getting node/npm/parcel working on the command line, so I swapped over to an ubuntu distro running in a windows VM. I’ll skip the pain, but there is a pretty good article about how to do that here.
After that, its fairly straightforward to get your project moving forward. You can follow this simple example here.
I’ve also been using VSCode which has a pretty slick integration with the ubuntu running in your virtual machine.
Building the Service
Create a new repository called medium-customer-manager
. Clone it locally and start a new branch first-service
.
Make sure that you have node and npm available on your commandline
brian@Redoubt:~/workspace/medium-customer-manager$ node --version
v13.12.0
brian@Redoubt:~/workspace/medium-customer-manager$ npm --version
6.14.4
I’m going to be roughly the tutorial here. Create an initial project layout with npm
npm i create-react-app
npx create-react-app medium-customer-manager
First make the “DTO” structure that will hold our customer object
in change src/App.js
to look like this:
import React, { Component } from 'react';
import Customers from './components/customers';
class App extends Component {
state = {
customers: []
}
componentDidMount() {
fetch(process.env.REACT_APP_CUSTOMER_HOST+'/customer/all', {
method: 'get',
headers: new Headers({
'Content-Type': 'application/json'
})
})
.then(res => res.json())
.then((data) => {
this.setState({ customers: data })
})
.catch(console.log)
}
render() {
return (
<Customers customers={this.state.customers} />
)
}
}
export default App;
This is our call to our service endpoint. We’re using an environment variable for the host, so that we can change it out. I think that this only works at build time though, so its not ideal and is only for short term integration. In this logic we’re grabbing the JSON from our list all customers endpoint and putting it in our state holder. Next we’re calling into our Customers page passing in this data, so lets look at that.
Create src/components/customers.js
and add this data:
import React from 'react'
const Customers = ({ customers }) => {
return (
<div>
<center><h1>Customer List</h1></center>
<table id="dtBasicExample" className="table table-striped table-bordered table-sm" cellSpacing="0" width="100%">
<thead>
<tr>
<th className="th-sm">CustomerId
</th>
<th className="th-sm">Customer Name
</th>
<th className="th-sm">Customer Email
</th>
<th className="th-sm">Customer Phone
</th>
</tr>
</thead>
<tbody>
{customers.map((customer) => (
<tr key={customer.customerId}>
<td>{customer.customerId}</td>
<td>{customer.firstName} {customer.lastName}</td>
<td>{customer.email}</td>
<td>{customer.phoneNumber}</td>
</tr>
))}
</tbody>
</table>
</div>
)
};
export default Customers
Its a simple table, but the important part is that we’re referring to this section as
Customers
which i believe helps react understand that its going to replace theCustomers
in theApp.js
with this html.
Next we need to review src/index.js
, which is the ‘master’ component. it should look like this:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Here we’re telling react to replace the root div element in index.html with the html rendered by App (which is generated by our App.js).
Now let’s take a look at public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Customer Management</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
Nothing crazy here… react is going to use index.js by default and follow our chain of logic down to the table render and replace the root div. I am using bootstrap here, just because I heard about it and thought it might be easier to write a multi-platform app with it.
I did remove a bunch of cruft that is automatically generated by create-react-app. I would probably use parcel in the future instead, but this is what got me going and I needed a quick win with the UI in order to build my confidence.
Create an environment variable to point to our customer-manager instance (running in the IDE)
export REACT_APP_CUSTOMER_HOST="http://localhost:10000"
now npm install to make sure that we have all of our dependencies
npm install
and start the app within the built in server
npm start
this should startup a browser window and you should see the customer list populated from any data that you may have previously saved.
Build and Commit
git checkout first-service
git add .
git commit -m "customer list page"
git push
git checkout master
git merge first-service
git push