-
Notifications
You must be signed in to change notification settings - Fork 0
Step 5: Project 3 MERN stack implementation
- MongoDB: A document-based, No-SQL database used to store application data in a form of documents.
- ExpressJS: A server side Web Application framework for Node.js.
- ReactJS: A frontend framework developed by Facebook. It is based on JavaScript, used to build User Interface (UI) components.
- Node.js: A JavaScript runtime environment. It is used to run JavaScript on a machine rather than in a browser.
Above is a recording of my terminal as I did this step, let me know your thoughts on doing this in the future!
Node was installed using:
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
The reported versions are Node v12.22.12 and npm 6.14.16
I then created a folder called Todo
and ran npm init
in there and filled out the form it provided. This is my configuration json:
{
"name": "todo",
"version": "1.0.0",
"description": "A todo application made as part of the Aker Academy",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\""
},
"author": "Madeline Marsh",
"license": "ISC" // Wasn't sure what to put here so I left it at default
}
Express was installed using npm install express
and it did throw a few warnings but ultimately did install correctly
The dotenv module was installed and threw one warning
But since this wasn't required I was expecting it anyway! Then I opened vim and entered in the index.js
code:
const express = require('express');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "\*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use((req, res, next) => {
res.send('Welcome to Express');
});
app.listen(port, () => {
console.log(`Server running on port ${port}`)
});
node index.js
was then run to start the server, the rules were updated in AWS to allow for connections on port 5000
And to verify I visited it in a web browser:
The routes
folder was created and vim was used to create api.js
const express = require ('express');
const router = express.Router();
router.get('/todos', (req, res, next) => {
});
router.post('/todos', (req, res, next) => {
});
router.delete('/todos/:id', (req, res, next) => {
})
module.exports = router;
Mongoose was installed withh npm install mongoose
. Then a new directory called models
was created and inside todo.js
was placed with the contents:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//create schema for todo
const TodoSchema = new Schema({
action: {
type: String,
required: [true, 'The todo text field is required']
}
})
//create model for todo
const Todo = mongoose.model('todo', TodoSchema);
module.exports = Todo;
The contents of the api.js
that was in the routes
directory was deleted and and replaced with:
const express = require ('express');
const router = express.Router();
const Todo = require('../models/todo');
router.get('/todos', (req, res, next) => {
//this will return all the data, exposing only the id and action field to the client
Todo.find({}, 'action')
.then(data => res.json(data))
.catch(next)
});
router.post('/todos', (req, res, next) => {
if(req.body.action){
Todo.create(req.body)
.then(data => res.json(data))
.catch(next)
}else {
res.json({
error: "The input field is empty"
})
}
});
router.delete('/todos/:id', (req, res, next) => {
Todo.findOneAndDelete({"_id": req.params.id})
.then(data => res.json(data))
.catch(next)
})
module.exports = router;
I signed up for an account on mongodb.com and allowed access to it by any IP address
The interface has changed a little bit since the instructions were written, I also had to select eu-west-1 as eu-west-2 is not on the free tier. I created a user for access:
Setup took a few minutes:
I then went to browse the collections and clicked "Add My Own Data"
I created a database and a collection:
The connection string was:
DB = 'mongodb+srv://user:<password>@cluster0.il62h.mongodb.net/aker?retryWrites=true&w=majority'
Replced the contents of index.js
with the following:
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const routes = require('./routes/api');
const path = require('path');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
//connect to the database
mongoose.connect(process.env.DB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log(`Database connected successfully`))
.catch(err => console.log(err));
//since mongoose promise is depreciated, we overide it with node's promise
mongoose.Promise = global.Promise;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "\*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(bodyParser.json());
app.use('/api', routes);
app.use((err, req, res, next) => {
console.log(err);
next();
});
app.listen(port, () => {
console.log(`Server running on port ${port}`)
});
And then started the server:
Using the API testing tool Postman I sent a message to the database (the same one that was in the writing for this task due to lack of creativity) and it appeared in the database after querying it:
To delete a task I simply sent a delete request to the /api/todo/
URL but I added the ID to the end of the end of the URL so it looked like this:
http://3.10.173.224:5000/api/todos/627a90bf5f4dc335f37b88af
npx create-react-app client
requires node 14 or higher so I updated node version with "n" which is a Node.js version manager.
sudo npm cache clean -f
sudo npm install -g n
sudo n stable
And now I'm running the latest version v16.15.0
Now I can run npx create-react-app client
. This ended up taking a few minutes
Concurrently was installed with npm install concurrently --save-dev
followed by nodemon npm install nodemon --save-dev
.
I then made the changes to package.json
:
{
"name": "todo",
"version": "1.0.0",
"description": "A todo application made as part of the Aker Academy",
"main": "index.js",
"author": "Madeline Marsh",
"scripts": {
"start": "node index.js",
"start-watch": "nodemon index.js",
"dev": "concurrently \"npm run start-watch\" \"cd client && npm start\""
},
"license": "ISC",
"dependencies": {
"create-react-app": "^5.0.1",
"dotenv": "^16.0.0",
"mongoose": "^6.3.3"
},
"devDependencies": {
"concurrently": "^7.1.0",
"nodemon": "^2.0.16"
}
}
I then moved to the client
directory and made the changes to the package.json
file which now reads:
{
"name": "client",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Running npm run dev
we get a successful start
As seen in the web browser:
Then I moved into /Todo/client/src/components/
and created three files with the touch
command, touch Input.js ListTodo.js Todo.js
. Then in Input.js
I entered this:
import React, { Component } from 'react';
import axios from 'axios';
class Input extends Component {
state = {
action: ""
}
addTodo = () => {
const task = {action: this.state.action}
if(task.action && task.action.length > 0){
axios.post('/api/todos', task)
.then(res => {
if(res.data){
this.props.getTodos();
this.setState({action: ""})
}
})
.catch(err => console.log(err))
}else {
console.log('input field required')
}
}
handleChange = (e) => {
this.setState({
action: e.target.value
})
}
render() {
let { action } = this.state;
return (
<div>
<input type="text" onChange={this.handleChange} value={action} />
<button onClick={this.addTodo}>add todo</button>
</div>
)
}
}
export default Input
Finally I went back to the client
folder and installed axios with npm install axios
In the src/components/
directory I opened ListTodo.js
and entered in the following:
import React from 'react';
const ListTodo = ({ todos, deleteTodo }) => {
return (
<ul>
{
todos &&
todos.length > 0 ?
(
todos.map(todo => {
return (
<li key={todo._id} onClick={() => deleteTodo(todo._id)}>{todo.action}</li>
)
})
)
:
(
<li>No todo(s) left</li>
)
}
</ul>
)
}
export default ListTodo
Then in todo.js
I entered this:
import React, {Component} from 'react';
import axios from 'axios';
import Input from './Input';
import ListTodo from './ListTodo';
class Todo extends Component {
state = {
todos: []
}
componentDidMount(){
this.getTodos();
}
getTodos = () => {
axios.get('/api/todos')
.then(res => {
if(res.data){
this.setState({
todos: res.data
})
}
})
.catch(err => console.log(err))
}
deleteTodo = (id) => {
axios.delete(`/api/todos/${id}`)
.then(res => {
if(res.data){
this.getTodos()
}
})
.catch(err => console.log(err))
}
render() {
let { todos } = this.state;
return(
<div>
<h1>My Todo(s)</h1>
<Input getTodos={this.getTodos}/>
<ListTodo todos={todos} deleteTodo={this.deleteTodo}/>
</div>
)
}
}
export default Todo;
Then back in the src
folder logo.svg
was deleted with rm -rf logo.svg
. Then the contents of App.js
was deleted and replaced with this:
import React from 'react';
import Todo from './components/Todo';
import './App.css';
const App = () => {
return (
<div className="App">
<Todo />
</div>
);
}
export default App;
Then App.css
was replaced with this:
.App {
text-align: center;
font-size: calc(10px + 2vmin);
width: 60%;
margin-left: auto;
margin-right: auto;
}
input {
height: 40px;
width: 50%;
border: none;
border-bottom: 2px #101113 solid;
background: none;
font-size: 1.5rem;
color: #787a80;
}
input:focus {
outline: none;
}
button {
width: 25%;
height: 45px;
border: none;
margin-left: 10px;
font-size: 25px;
background: #101113;
border-radius: 5px;
color: #787a80;
cursor: pointer;
}
button:focus {
outline: none;
}
ul {
list-style: none;
text-align: left;
padding: 15px;
background: #171a1f;
border-radius: 5px;
}
li {
padding: 15px;
font-size: 1.5rem;
margin-bottom: 15px;
background: #282c34;
border-radius: 5px;
overflow-wrap: break-word;
cursor: pointer;
}
@media only screen and (min-width: 300px) {
.App {
width: 80%;
}
input {
width: 100%
}
button {
width: 100%;
margin-top: 15px;
margin-left: 0;
}
}
@media only screen and (min-width: 640px) {
.App {
width: 60%;
}
input {
width: 50%;
}
button {
width: 30%;
margin-left: 10px;
margin-top: 0;
}
}
Finally index.css
was replaced with this:
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
background-color: #282c34;
color: #787a80;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
In the Todo
directory I ran npm run dev
. The site loaded successfully, however, there were no entries in the database because I had deleted them in the previous task