Full-Stack JavaScript Application

In this guide, let's build a Full-Stack JavaScript application from scratch and deploy it on server.

Follow the steps in the outline of this document (on right side) to get started.


Services

We'll have following three services in our application:

  1. API (REST)
    1. Node
    2. Express
    3. Mongoose
  2. Web (single page application)
    1. React
    2. React Router
  3. Database
    1. MongoDB

1. API

Let's create REST API with Node, Express, and Mongoose.

We've already created a repository deploymonk/fullstack-javascript-api for you to get started quickly.

1. Express

Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

Use the application generator tool, express-generator, to quickly create an Express application skeleton:

npx express-generator api

Test the installation by running it locally:

cd api
npm install
npm start

Goto browser and type localhost:3000, Your server is ready if you get the result below:

Express

2. Mongoose

Mongoose is a MongoDB object modeling(ODM) tool which provides a straight-forward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks and more, out of the box.

Type the following command to install Mongoose:

cd api
npm install mongoose --save

To connect to your database using Mongoose, add following lines in your api/app.js:

// File: api/app.js
var mongoose = require("mongoose");
// ...
const DATABASE_URL =
process.env.DATABASE_URL || "mongodb://localhost/fullstack";
mongoose
.connect(DATABASE_URL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("connection successful"))
.catch((err) => console.error(err));

Enable CORS:

npm install cors --save
// File: api/app.js
var cors = require("cors");
// ...
app.use(cors());

3. Model

Create Product Mongoose model:

cd api
mkdir models
touch models/Product.js

Define Product model schema api/models/Product.js:

// File: api/models/Product.js
const mongoose = require("mongoose");
const ProductSchema = new mongoose.Schema({
name: String,
price: Number,
});
module.exports = mongoose.model("Product", ProductSchema);

4. Routes

Let's create following routes for REST API endpoints:

MethodEndpointDescription
GET/productGet all products
POST/productAdd product

Create a new route file routes/products.js:

cd api
touch routes/products.js

Create functions for each product REST API endpoint:

// File: api/routes/products.js
const express = require("express");
const router = express.Router();
const Product = require("../models/Product.js");
// Get all products
router.get("/", function (req, res, next) {
Product.find(function (err, products) {
if (err) return next(err);
res.json(products);
});
});
// Add product
router.post("/", function (req, res, next) {
Product.create(req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
module.exports = router;

Enable the product routes by adding it to api/app.js

// File: api/app.js
// ...
const productsRouter = require("./routes/products");
// ...
app.use("/products", productsRouter);

Test the REST API endpoints by sending a curl request:

Create product:

curl localhost:3000/products \
-H 'Content-type: application/json' \
-d '{"name": "Product", "price": 1500}'

Get all products:

curl localhost:3000/products \
-H 'Content-type: application/json'

5. Git repository

Now that we have the API setup, lets push the code to git repository (VCS account eg: GitHub or GitLab):

  1. Go to your VCS account website
  2. Create a new repository
  3. Copy the Git URL (eg: git@github.com:username/example.git)

Initialize git repository and commit files:

cd api
git init
git add .
git commit -m "First commit"

Add URL for the remote repository and push the changes:

git remote add origin git@github.com:username/example.git
git push -u origin master

2. Web

Let's create website using React.

We've already created a repository deploymonk/fullstack-javascript-web for you to get started quickly.

1. React

React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and companies. React can be used as a base in the development of single-page or mobile application.

Let's bootstrap a React application using Create React App

Create React App allows you to quickly create and run React applications with no configuration.

Open a terminal locally and enter following command to create a basic CRA application:

npx create-react-app web

Test the installation by running it locally:

cd web
npm start

2. React Router

Install React Router for routing inside browser:

React Router is a collection of navigational components that compose declaratively with your application.
cd web
npm install react-router-dom --save

3. Basic setup

Clean up the default files:

cd web/src/
rm App.js App.css App.test.js logo.svg

Create config.js file to supply URL_API which will be used in fetch calls:

// File: web/src/config.js
export const URL_API = process.env.REACT_APP_URL_API;

4. Routes and components

Let's create the routes and components for listing and adding products

Create App component web/src/App.js:

// File: web/src/App.js
import React from "react";
import { BrowserRouter, Switch, Route, Link } from "react-router-dom";
import Home from "./Home";
import ProductList from "./ProductList";
import ProductAdd from "./ProductAdd";
export default function App() {
return (
<BrowserRouter>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/product/list">Products</Link>
</li>
<li>
<Link to="/product/add">Product Add</Link>
</li>
</ul>
</nav>
<hr />
<main>
<Switch>
<Route path="/product/list">
<ProductList />
</Route>
<Route path="/product/add">
<ProductAdd />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</main>
</div>
</BrowserRouter>
);
}

Create Home component web/src/Home.js:

// File: web/src/Home.js
import React from "react";
export default function Home() {
return (
<div>
<h1>Welcome!</h1>
</div>
);
}

Create Product List component web/src/ProductList.js:

// File: web/src/ProductList.js
import React, { useState, useEffect } from "react";
import { URL_API } from "./config";
const ProductList = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
refresh();
}, []);
const refresh = async () => {
try {
const response = await fetch(URL_API + "/products");
const products = await response.json();
setProducts(products);
} catch (error) {
console.log(error);
}
};
return (
<div>
<h1>Product List</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{products.map((product) => (
<tr key={product._id}>
<td>{product.name}</td>
<td>{product.price}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default ProductList;

Create Product Add component web/src/ProductAdd.js:

// File: web/src/ProductAdd.js
import React, { useState } from "react";
import { withRouter } from "react-router";
import { URL_API } from "./config";
const ProductAdd = ({ history }) => {
const [product, setProduct] = useState({ name: "", price: "" });
console.log(history);
const onSubmit = async (event) => {
event.preventDefault();
try {
const response = await fetch(URL_API + "/products", {
headers: { "Content-type": "application/json" },
method: "POST",
body: JSON.stringify(product),
});
const result = await response.json();
if (result && result._id) {
history.push("/product/list");
}
} catch (error) {
console.log(error);
}
};
const onChange = (event) => {
const { name, value } = event.target;
setProduct({ ...product, [name]: value });
};
return (
<div>
<h1>Product Add</h1>
<form onSubmit={onSubmit}>
<p>
Name:
<input
type="text"
name="name"
value={product.name}
onChange={onChange}
required
autoFocus
/>
</p>
<p>
Price:
<input
type="number"
name="price"
value={product.price}
onChange={onChange}
required
/>
</p>
<p>
<button type="submit">Submit</button>
</p>
</form>
</div>
);
};
export default withRouter(ProductAdd);

5. Preview

Home page:

Product list page:

Product add page:

6. Git repository

Now that we have the Web setup, lets push the code to git repository (VCS account eg: GitHub or GitLab):

  1. Go to your VCS account website
  2. Create a new repository
  3. Copy the Git URL (eg: git@github.com:username/example.git)

Initialize git repository and commit files:

cd api
git init
git add .
git commit -m "First commit"

Add URL for the remote repository and push the changes:

git remote add origin git@github.com:username/example.git
git push -u origin master

3. Database

To develop locally, you'll need to install MongoDB on your system.

For live, you can either deploy a MongoDB service via Deploy Monk or use free tier of MongoDB Atlas.


Deployment

Now that we've services ready, let's go ahead and deploy the Full-Stack JavaScript Application using Deploy Monk.

1. Console

If you have an account already then go ahead and Login and access your Console.

In case you are new to Deploy Monk, go ahead and Signup for a free account.

2. Create project

Create a new project by going to your ConsoleProjectsNew Project and enter the following:

  1. Project name - enter Full-Stack JavaScript
  2. Cloud account - select your cloud account where you'd like the server to be hosted. If you have not yet connected a cloud account, then first connect it by going to ConsoleCloud
  3. Server size - select the size of server

Click on Save button to create a project. It may take upto 1 minute for the server to be provisioned by your cloud provider.

Project

Once created, you'll see the project information along with IP Address:

Project

3. Create services

Once the project is created and server is provisioned by the cloud provider successfully, you can now add services to your project.

1. Database

To create a MongoDB database service, click on New Service button and fill in the information as follows:

  1. Service type - select Database
  2. Database type - select MongoDB
  3. Service name - enter database
  4. Advance configurations,
    1. Port - enter port value (optional, default 27017)
    2. Database user - enter database user (eg: user)
    3. Database password - enter a database password (tip: use passwordsgenerator.net to generate random secure password)

To learn more about MongoDB service, goto Database / MongoDB section of this documentation website.

Now click on Save button to create the new service.

Project Service Database

2. API

To create an API service, click on New Service button and fill in the information as follows:

  1. Service type - select Backend

  2. Language - select Node

  3. Service name - enter a api

  4. Repository - select the repository created in created for API service

  5. Advance configurations,

    1. Port - enter 3000
    2. Start command - enter this value npm run start
    3. Environment Variables - enter following:
PORT=3000
DATABASE_URL=mongodb://<USER>:<PASSWORD>@<IP_ADDRESS>:27017/fullstack?authSource=admin

Replace following:

  1. <USER> - with your database user created when added Database service
  2. <PASSWORD> - with your database password created when added Database service
  3. <IP_ADDRESS> - IP address of the project server

To learn more about backend service, goto Backend section of this documentation website.

Now click on Save button to create the new service.

Project Service API

3. Web

To create an Web service, click on New Service button and fill in the information as follows:

  1. Service type - select Frontend

  2. Framework - select React

  3. Service name - enter a web

  4. Repository - select the repository created in created for API service

  5. Advance configurations,

    1. Port - enter 4000
    2. Build command - enter this value npm run build
    3. Environment Variables - enter following:
PORT=4000
REACT_APP_URL_API=http://<IP_ADDRESS>:3000

Replace following:

  1. <IP_ADDRESS> - IP address of the project server

To learn more about frontend service, goto Frontend section of this documentation website.

Now click on Save button to create the new service.

Project Service API

4. Deploying

Click on DEPLOY button to deploy the project and go live!

Project

Whenever you make any changes to any of your services configuration or code repository, deploy the changes to the server by just clicking on DEPLOY again.

5. Domain name and SSL

Till now our services were running on IP address. You can instead attach domain or sub domain to your services, for which, Deploy Monk provisions SSL certificate for free making your services secure to use.

1. Get a domain name

Before you can attach domain name to the services, you'll need to buy the domain name. Some domain providers you can buy domains from are:

  1. Google Domains
  2. Namecheap
  3. GoDaddy
  4. HostGator

2. DNS entries

After buying your domain, open the DNS settings for your domain. In case of Google Domains, the settings can be found at https://domains.google.com/m/registrar/example.com/dns

You'll need to modify Custom resource records for your domain.

Let's consider following example:

  1. Server IP address: 167.172.147.194 (this is available in your Console's Project page)
  2. Domain name: example.com

In Full-Stack JavaScript Application services case, make the following entry for each service:

Service TypeDNS NameDNS TypeDNS Data
Web@A167.172.147.194
APIapiA167.172.147.194

Note: It may take upto 24 hours for the changes to reflect, however in most cases it should work within 5 minutes after making the DNS settings changes. You can verify the DNS changes using MX Toolbox's DNS Lookup portal.

Example from Google Domain DNS settings page:

3. Updating services and re-deploying

Once you've the domain purchased and the DNS settings verified, open the project details by going to your ConsoleProjects.

For each service update following configurations:

  1. Web
    • Advance configurations
      1. URL - enter https://example.com
    • Click on Save button to save changes.
  2. API
    • Advance configurations
      1. URL - enter https://api.example.com
    • Click on Save button to save changes.

Now click on DEPLOY to deploy the changes to the server. You'll now be able to access your services using the applied domains.