The following tutorial should help in everything from basic knowledge of forms until the deployment phase.
Click Here, for the PPT.
- Create a Static page in HTML/Bootstrap.
- Create scaffolds for Server and client apps. Can just copy the folders & files already in this repo!Try starting both the server and client.
- Add your html page you added in step 1 into your react app.
- Deploy to EC2.
- Add an API in your application which accepts name and description and saves it to the Database. Then test this API
- Redeploy! and check if it works online!
- Add API in the server which accepts a username and a password and returns {"Auth": "Success"} as Output (Refer to the section about addition api)
- Consume that API in the client-side ReactJS app. (Refer to the section where making a simple addition API is discussed)
- Create a form which has inputs for name, description & sends the data to the previously developed API.
- Modify reactJS component such that the form mentioned in earlier step is only visible when the user has successfully logged in. ( Use conditional rendering)
- Test if saving name and decription works now!
Design diagrams for the project, along with a sample sequence diagram for general React Component.
First this is a simple Hello world HTML page
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</body>
</html>
Now consider, we have to ask user to enter Name. We can add the following inside the body tag :
<form action="/endpoint" method="post">
<label for="firstName">First Name</label>
<input type="text" name="firstName" id="firstName">
<label for="lastName">Last Name</label>
<input type="text" name="lastName" id="lastName">
<input type="submit" value="Save">
</form>
This would result in the follwing html.
The user can fill in the information and click on "Save" when done.
now, What happens when this button is pressed?
The browser sends the form data to the endpoint specified in theaction
parameter of the form.
There are times when this behaviour is not enough, we might have to perform some sanity check before sending the data to the backend. Hence we can intercept this action of the browser sending data to the server using Javascript.
For Eg. I need to alert the user with firstName,lastName What can be done is, we can add the following script tag after we load jQuery.
<script>
$(document).ready(function () {
$("#firstForm").on('submit', function (e) {
e.preventDefault(); //prevent submit
//Access Form info
myFormObject = new FormData(document.getElementById("firstForm"));
alert("you enetered " +
myFormObject.get('firstName') + ", " +
myFormObject.get('lastName'));
});
});
</script>
Now since we know how to intercept the default behaviour of the form, we can use it to modify the form object to suit our needs, THEN send the POST / GET request via JavaScript.
- Controlling ASync function calls.\
- JS is single thereaded, blocking & Synchronous. It SUPPORTS async behaviour.(i.e. some functions are defined as async)
- It essentially has an execution stack and an event queue.
- Things are added to this execution stack, but messages can be left into the event queue in async way.
- The job of the Event loop is to look into the call stack and determine if the call stack is empty or not. If the call stack is empty, it looks into the message queue to see if there’s any pending callback waiting to be executed. Unlike other languages, if 2 function calls are made after each other, Function callback is a process of one function calling the other function once the first function is done it's job. We can use Callbacks or Promises to sync function calls. \
Essentially, when we call an external API / make a DB call, it does not make sense for a language to keep the function on the top of the execution stack. Hence instead of keeping it in the top of the stack, we have the power to instruct browser engine / nodejs to put a message in the event queue once it is done fetching the DB or api call.
-
Promises
Promises can be used to make async code synced. Here is a good answer for using Promises with ReactJS. -
Variables, Functions & Es6 syntax. varibles can be declared in various ways. You can use
var, let or const
keywords to declare a variable. They differ by the scope.var
has function scope,let
&const
have block scope. As name might suggest aconst
creates immutable variables.
ES6 classes can be used to delcare classes in JS. You can have inheritance in ES6 classes. A good example for everything in these classes : es6 class sample\
You can declare functions using the arrow
syntax as well. These functions are same to the functions declared by using the function
keyword.
Eg: These are equavalent
// Function Expression
const doubleEXP = function(num){
return num * 2;
}
// Arrow Function
const doubleARR = (num) => {
return num * 2;
}
References
Understanding async javascript
When is JS async
What the hell is a promise
var, let & const
Arrow Functions
W3Schools - Extreme basic JS reference
Let's create a server. Essentially follow Expressjs Installation guide & Hello World Guide
- cd to a new folder
npm init
to initialize a new NPM project.- Hit enter for everything.
- run
npm install express --save
This will save expressjs as a depedency in your package.json file. - Create a file
index.js
Enter hello world code there
const express = require('express') // IMPORT express
const app = express() // create an instance of the import.
const port = 3000 // variable to store the port to listen on
app.get('/', (req, res) => res.send('Hello World!')) // IF you get a GET on '/' send 'Hello World' as a response'
app.listen(port, () => console.log(`Example app listening on port ${port}!`)) // make app listen on the port.
You just created a simple Server. That returns Hello world when you ask '/'! Now you can use any of the clients to request this server.
Important highlights :
app.get
=> accepts 2 parameters. one the path of the request and other the function to run when the path string is received. In our case the path is root or '/' the function is defined in thearrow
function format.req,res
=> the fuction is passed 2 arguments :req
corresponds to the request. This object can be used to extract request parameters.res
defines the modifyable object which will be given as a response. In our case, we are not looking for any vairebles from the request and just sending "Hello World!" as a response.
You can certainly add everyting, but in general an express app has typical skeleton, we can use generator to scaffold out basic functions. We will use npx
for this.
- cd to a new directory
- run
npx express-generator --view ejs
.This will scaffold an express app. - Observe the files.
app.js
is the start point of the app. It contains basic code to parse requests and imports to indexRoutes and userRoutes. index.js
(one of the controllers) in routes folder contains the code to be invoked when any request hits the server on root domain. An example flow is :
- A client hits the "/" endpoint.
- The expressJS app parses the input and populates the request information in the
req
variable (1st argument) & ares
variable(2nd argument). - ExpressJS matches the endpoint to existing routes that any controller (or route ) can serve.
- If the match is found, that controller is invoked.
- The function in this controller is responsible to populate the
res
object which was passed when calling the said function. - The response is hence sent to the user.
You can run this using command npm install
followed by DEBUG=myapp:* npm start
(unix) or set DEBUG=myapp:* & npm start
(Windows). Checkout http://localhost:3000 & http://localhost:3000/users. The logic to be sent in the requests is written in the router defined in index.js
and users.js
respectfully.
We want the server to respond with Hello <your Name>
when the server receives a POST request on /hello
i.e. http://localhost:3000/hello
. let's modify the index.js controller.
router.post('/hello', (req,res)=>{
res.json({"Greeting" : "Hello " + req.body.name});
});
We can call this API from any of the clients. Here is a snip of the postman client.
We can also call these requests from a Javascript method in HTML file you created. We will use axios to do this job in ReactJS app. You can also use the Browser native or any other library to do the ajax call.
Consider a simple API where we want the server to give us the result of addition of 2 nos.
Stop and Think how you would implement this...
One way is to add the following function in index.js
:
//note, unlike POST, parameters are visible to a user in browser address bar in GET request.
router.get('/add/:firstNumber/and/:secondNumber', (req,res)=>{
console.log(req.params.firstNumber + req.params.secondNumber);
//Checkout console to see why parseInt is essential in this case.
let firstNo = parseInt(req.params.firstNumber),
secondNo = parseInt(req.params.secondNumber);
res.json({"Addition" : firstNo + secondNo});
});
We could have done this exactly the way we did the previous technique ( using POST request and res.body
) but this is just another way to demo the params
attribute. You can see basic routing info on Express Docs- Routing
let's invoke this using other client: your browser. Visit http://localhost:3000/add/1/and/2.
Follow ExpressJS instructions to add MongoDB.
you do not need to install mongoDB locally (if you have some storage limitations). You can just use MongoDB Atlas to get a free cloud hosted DB and use the internet to connect to that DB instead of the localhost:27017
you use in the instructions in that case.
You can use Atlas to save your time as you do not need any steps to install atlas on your EC2 instance too.
Just for reference you can use direct MongoDB queries off Express. To Insert:
db.users.insert( { name: "ninad", web : "https://ninadtech.com" } );
To find:
db.users.find({name : "ninad"});
To update:
db.users.updateOne({name : "ninad" , $set: {"web" : "https://ninadpchaudhari.github.io"}})
You can find all the basic operations on MongoDB docs or just google about basic commands.
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. To allow CORS follow instructions here. Basically
npm install --save cors
addapp.use(cors());
inapp.js
Express References
[Express Routing-easy](https://expressjs.com/en/starter/basic-routing.html)\ [Express routing-docs](https://expressjs.com/en/guide/routing.html)Find "Motivation" in slides
First of all, we should understand the fact that: web apps are written in Javascript and HTML/CSS is just a technology that acts as a View
or templates and styles which JS can change anytime.
I was going to expand on getting started with ReactJS here, but this article by Tania Rascia explans it very well. Please give it a read at-least until the section of "JSX".
I recommend the "Main Concepts" section of docs. Skip the advanced concepts like Lifting State up (chapter 10).
Another wonderful resource is the w3school tutorial Follow, read and understand each line till "lifecycle".
For our Assignment, you can just use npx
method to create a front-end app. You could have a single component or can make 3 components navbar, profile page and then the admin page. Then render them based on if the user is logged in.
Glance over Thinking in React If you are a little comfortable after the previous articles and a small app.
- A react app is a collection of components.
- Each component can optionally have memory (called state).
- Each component has to have a
render
method, which is responsible to act as aView
to show something on the UI. - Imagine each component being refreshed everytime you update the
state
. - if you wish to run something before the component is mounted, use the specific lifecycle method.
- Checkout w3schools-forms. They have an example on how to invoke a specific method on submit.
- You can use axios-tutorial or use the built in
fetch
as explained by tania. - You can import the
div containers
from bootstrap to react by copying them to therender
method.
I am going to be brief here assuming you have read the links earlier. cd to the project directory, then this command will create a new client-side project in the folder my-client
, then I am clearing all the files in src. Then adding bootstrap to the project.
npx create-react-app my-client
cd my-client/src && rm -f * && cd ..
npm install --save bootstrap
Now, starting form scratch, Adding just 1 file : index.js
in the src directory. Adding required imports and creating a component.
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
class MyFirstComponent extends React.Component{
render(){
return (
<h1>Hello!
<small className="text-muted">With faded secondary world!</small>
</h1>
);
}
}
Note now : We have just 1 file in the project source!
Just One JS file. We are developing for web without focusing on HTML. This JS component happens to have a method that returns a JSX expression (something like HTML).
Also, I am using bootstrap classes using className
instead of just class
. React compiles this for you. This is required for JSX. Read references if you have the curiosity for why.
This component can be rendered into a place, which we will provide the markup for in the index.html
( We just have one div there with ID = root, which are using as a first rendering target -- checkout index.html).Then add the following code to index.js to make it render the MyFirstComponent
into the division with id=root
\
ReactDOM.render(, document.getElementById("root"));
npm start
Visit http://localhost:3000
note: as you make changes in code, the same is auto reflected to your browser.
Please read about state. It is a bunch of variables a component can store. You update variables in state by calling the .setState(Object)
method. It takes a Object as a parameter the syntax is : { : } Checkout the way W3c School's form handling for reference or my addition form.
please refer to the w3school forms for more info. Before we begin, I am copying my-client
into a folder my-addition-client
so the previous section can be reffered.
- Create a new component named
MyAdditionForm
in a seperate file. - Write code to update this component's
state
when user makes any change. - Write code to call the backend addition api using built in
fetch
. - Write code to display the result.
- Export the same as a
module
and import into theindex.js
file. - Use this new component inside the
MyFirstComponent
just to demo that one component can be inside the other, without affecting much. - Make expressjs listen on port
9000
and start it. As only one process can listen to one port at one time, this is important.DEBUG=myapp:* PORT=9000 npm start
- Test out my react app.
Note: Usually for complex UI components, you can develop the HTML in seperate file, modify layout/style etc, then import into the app's render method. You do not need to export and import the way I am doing in point 2, you can just create the component in same file. I am doing it to make it easier to read.
Find "Motivation" in slides I have modified code by mohamed Kheliif in the tutorial Uploading files from a React app to AWS the right way. You can checkout the article for explanation, I have modified files to make sure they run in 2020 and have added files in project files.
- Download Access Keys
- Sign into Console.
- Click on Your name on right top.
- Select
My Security Crednetials
- Click on
Access Keys
- Click
Create new access key
- Open the CSV file. Note the AWSSecretKey and AWSAccessKeyId.
- Create a new S3 bucket,
- Search for S3 in AWS console.
- Click on
new
- give a dnc compliant name for your bucket.
- Next... until you reach
Block public access
. - Untick
Block all public access
and accept theacknowledgment
- Modify the bucket to allow writes and public reads.
- Click on the bucket you created earlier
- Click on the
Permissions
tab. - Goto the
CORS
Configuration. - Enter
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
- Click
Save
- add
.env
file to.gitignore
- create a new file
.env
in your server directory (same level as app.js). - Configure your app to read this file
require('dotenv').config();
- Populate the keys with your info from CSV file
AWSAccessKeyId=YourKey AWSSecretKey=YourSecret bucket=YourBucketName
- Checkout the file
awsCtrl.js
. You can just use the same code. - use this new controller in the app :
app.use('/sign_s3', awsCtrl);
. - You can test this new api avaliable at
/sign_s3
in postman, by calling a POST request withfileName
andfileType
in request body.
You can run the app, by first ensuring your expressjs server is running on port 9000. Then running npm install && npm start
for this folder
- Just added a new JSX component in
MyS3UploadComponent.jsx
. - used it in the FirstComponent the same way we did addition component.
Either use Atlas or, Follow steps to install MongoDB on ubuntu on mongoDB documentation. Just follow the 1-4 steps. No need to do the optional steps.
Followed by following the steps to start MongoDB. i.e. sudo systemctl start mongod
- Open port
9000
to internet using security-group. - You can login into EC2, install git, node.
- Clone your project into any directory
git clone https://<USERNAME>:<PASSWORD>@github.com/path/to/repo.git
- run
npm install
to download all dependencies. - Install pm2 using
npm install pm2@latest -g
cd
to the directory where you have cloned your project.- run
PORT=9000 pm2 start ./bin/www
. This will start your ExpressJS server at port 900 and keep it running in background even after you log out. - You can see your server logs using
pm2 logs www
. You can checkout all commands pm2 can accept on pm2-managing processes. - Try accessing on http://YourEc2Address:9000.
- Make sure you are happy with your app. Remember, you are communicating your backend server at http://localhost:9000. You need to change that to the URL of your EC2.
- run
npm run-script build
. This will compile your JSX modules to a static website, servable by Nginx. - goto client folder in your cloned repo, then run
npm install && npm run-script build
on server, then copy the contents ofbuild
folder to/var/www/html
. Ensure you delete the index.debian.html file in that folder first. - You can also copy the contents of
build
folder on your PC to/var/www/html
. TIP : You can zip before upload usingzip -r build.zip build/
That way it is faster. - If you are using BrowserRouter in react app: modify
location /
block in nginx's default block in/etc/nginx/sites-enabled/default
to havetry_files $uri $uri/ /index.html;
You are always recomended to use HashRouter if you do not want to miodify this :)
You should now be able to access your React app over at EC2 address and server at port 9000.