An introduction to building a web application with itsa-react-server

Itsa-react-server is a MVC framework for building server side rendered single page apps (SPAs) with React.js. In case you missed it: see here.
This article is about how to build a RESTful web application
Setup
Before you start, you need to define your project in a new folder. Because itsa-react-server requires a several pieces (files and folders) to be there, the best thing to do, is to create a new project with our command line interface itsa-cli. We assume you already have node.js (version 8+) and npm installed before you proceed.
Install itsa-cli
npm install -g itsa-cliYou only need to do this once. After that you can create as many projects you want.
Create a new project
From within your projects folder, go to the terminal and type:
itsa create appname
appname is the name of the folder where your new application/project gets stored.
Now you already have a whole directory structure and a working web application. To view your new application, you will need to build and run it.
Build and run your app
Your new app needs to be built first. Itsa-react-server has multiple ways to do this, depending on your needs:
Just building:
npm run build
Just running the application (using the last build):
npm run server
Build and run the application:
npm run start
For development: build, run and watch for changes:
npm run watch
The console shows that you can view your app on http://localhost:3001. Congratulations: you have built a working web app with itsa-react-server!
Creating the pages (views)
Itsa-react-server is designed to have dedicated view for every webpage. A visitor can load these pages by specific urls, which we will explain later on (routes). The pages that the project needs are views for the MVC framework. Basically, they are just React.js Components that will receive the proper this.props (more about that later).
The default project already has 3 views. Let us just focus on index.jsx and information.jsx and manipulate them a bit. Note that any relative file reference starts from within the src directory (or node_modules).
src/views/index.jsx:
import 'assets/css/index.scss';import React from 'react';
import PropTypes from 'prop-types';
import Menu from 'client-modules/menu.jsx';class PageIndex extends React.Component {
render() {
return (
<div>
<Menu appProps={this.props.__appProps} />
<h1>Page index.html</h1>
{this.props.pagecontent}
</div>
);
}
}PageIndex.propTypes = {
__appProps: PropTypes.object,
pagecontent: PropTypes.string
};export default PageIndex;
src/views/information.jsx:
import 'assets/css/information.scss';import React from 'react';
import PropTypes from 'prop-types';
import Menu from 'client-modules/menu.jsx';class PageInformation extends React.Component {
render() {
return (
<div>
<Menu appProps={this.props.__appProps} />
<h1>Page information.html</h1>
{this.props.pagecontent}
</div>
);
}
}PageInformation.propTypes = {
__appProps: PropTypes.object,
pagecontent: PropTypes.string
};export default PageInformation;
As you might have noticed, we refer to (import) 3 files that we need to define as well. Actually, client-modules/menu.jsx itself refers to assets/css/client-modules/menu.scss, so lets create those 4 files:
src/assets/css/index.scss:
h1 {
color: #900;
}src/assets/css/information.scss
h1 {
color: #009;
}src/assets/css/client-modules/menu.scss
.mymenu {
font-size: 1.25em;
}src/client-modules/menu.jsx:
import 'assets/css/client-modules/menu.scss';const React = require('react'),
PropTypes = require('prop-types'),
MENU_ITEMS = [
{label: 'home', link: '/'},
{label: 'information', link: '/information'}
];class Menu extends React.Component {
render() {
const menuitems = this.renderMenuItems(); return (
<div className="mymenu pure-menu pure-menu-horizontal">
<ul className="pure-menu-list">
{menuitems}
</ul>
</div>
);
} renderMenuItems() {
const appProps = this.props.appProps,
currentPath = appProps.path; return MENU_ITEMS.map(menuitem => {
let classname = 'pure-menu-item';
const link = appProps.langprefix+menuitem.link,
label = menuitem.label; if (currentPath===menuitem.link) {
classname += ' pure-menu-selected';
} return (
<li
className={classname}
key={link}>
<a className="pure-menu-link" href={link}>
{label}
</a>
</li>
);
});
}
}Menu.propTypes = {
appProps: PropTypes.object
};export default Menu;
As you may have noticed, we are using styles from purecss. We need to make sure our project loads it on every page. Itsa-react-server makes this very easy: install the library and make a reference in the web app config file (src/manifest.json) as an external module:
install purecss:
npm i -S purecss
update src/manifest.json (also for the different environments):
{
...
"external-modules": [
"purecss/build/pure-min.css",
...
],
...
"environments": {
"local": {
...
"external-modules": [
"purecss/build/pure-min.css",
...
],
...
},
...
"development": {
...
"external-modules": [
"purecss/build/pure-min.css",
...
],
...
},
}
...
}Creating the models
Every view needs a “this.props” as a model, because the views are React.js Components. A view always gets a basic model that holds some specific server and client information in the member __appProps. The basic model looks like this:
{
__appProps: {
...
}
}This model can be extended with properties that you want to add. You can do this by defining a related file inside the models folder. Every view that finds a matching file (with same filename, .js) inside de models folder, will use this model and extend it to the basic object.
In our project, we will (re)define 2 files:
Note: itsa-react-server requires CommonJS style instead of ES6 modules for the models.
models/index.js:
const model = async () => {
return {
pagecontent: 'Hello World!'
};
};module.exports = model;
models.information.js:
const model = async () => {
return {
pagecontent: 'This is some extra information'
};
};module.exports = model;
Note: you can await (Promise) inside the models. This is handy f.e. when reading from a database. The view (and therefore the server-response) will wait for the Model data asynchronously.
Defining the routes
itsa-react-server assumes that every page can be requested RESTful with an unique url. We only need to make sure that we bind the urls to the proper views. Therefore, we maintain the file src/routes.js:
src/routes.js:
var routes = [
{
method: 'GET',
path: '/',
handler: function(request, reply) {
reply.reactview('index');
}
},{
method: 'GET',
path: '/information',
handler: function(request, reply) {
reply.reactview('information');
}
}];module.exports = routes;
Note: reply.reactview() is a special method defined by itsa-react-server. Its only argument is the name of the view (without .jsx). You may set up a sub directory structure inside the views folder, in which case the argument should have the foldername specified. F.e. views/projects/project1.jsx can be referred to as reply.reactview(‘projects/project1’)
Showing the results
From the terminal (command prompt), inside the root of your web app folder, you can built and start the app:
npm run watch
Now open the browser and go to http://localhost:3001
The webpage should be up and running! You can make changes now and save them: itsa-react-server will rebuild and the changes are updated in the browser automatically.
In the next article, I will discuss how the client can interact with the server.
