Sample React-Express-MongoDB (#59)
Signed-off-by: Afzal <sah.afzal@gmail.com>
This commit is contained in:
parent
3599a2e685
commit
2f750eb4f7
1
react-express-mongodb/.dockerignore
Normal file
1
react-express-mongodb/.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
server/node_modules
|
4
react-express-mongodb/.gitignore
vendored
Normal file
4
react-express-mongodb/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
frontend/node_modules/
|
||||||
|
server/node_modules/
|
||||||
|
.idea/
|
||||||
|
data
|
134
react-express-mongodb/README.md
Normal file
134
react-express-mongodb/README.md
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
## Compose sample application
|
||||||
|
### React application with a NodeJS backend and a MongoDB database
|
||||||
|
|
||||||
|
Project structure:
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── backend
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ...
|
||||||
|
├── docker-compose.yaml
|
||||||
|
├── frontend
|
||||||
|
│ ├── ...
|
||||||
|
│ └── Dockerfile
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
[_docker-compose.yaml_](docker-compose.yaml)
|
||||||
|
```
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: frontend
|
||||||
|
...
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
...
|
||||||
|
server:
|
||||||
|
container_name: server
|
||||||
|
restart: always
|
||||||
|
build:
|
||||||
|
context: server
|
||||||
|
args:
|
||||||
|
NODE_PORT: 3000
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
...
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
mongo:
|
||||||
|
container_name: mongo
|
||||||
|
restart: always
|
||||||
|
...
|
||||||
|
```
|
||||||
|
The compose file defines an application with three services `frontend`, `backend` and `db`.
|
||||||
|
When deploying the application, docker-compose maps port 5000 of the frontend service container to port 5000 of the host as specified in the file.
|
||||||
|
Make sure port 5000 on the host is not already being in use.
|
||||||
|
|
||||||
|
## Deploy with docker-compose
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker-compose up -d
|
||||||
|
Creating network "react-express-mongodb_default" with the default driver
|
||||||
|
Building frontend
|
||||||
|
Step 1/9 : FROM node:13.13.0-stretch-slim
|
||||||
|
---> aa6432763c11
|
||||||
|
...
|
||||||
|
Successfully tagged react-express-mongodb_app:latest
|
||||||
|
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
|
||||||
|
Creating frontend ... done
|
||||||
|
Creating mongo ... done
|
||||||
|
Creating app ... done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Expected result
|
||||||
|
|
||||||
|
Listing containers must show containers running and the port mapping as below:
|
||||||
|
```
|
||||||
|
$ docker ps
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
06e606d69a0e react-express-mongodb_server "docker-entrypoint.s…" 23 minutes ago Up 23 minutes 0.0.0.0:3000->3000/tcp server
|
||||||
|
ff56585e1db4 react-express-mongodb_frontend "docker-entrypoint.s…" 23 minutes ago Up 23 minutes 0.0.0.0:5000->5000/tcp frontend
|
||||||
|
a1f321f06490 mongo:4.2.0 "docker-entrypoint.s…" 23 minutes ago Up 23 minutes 0.0.0.0:27017->27017/tcp mongo
|
||||||
|
```
|
||||||
|
|
||||||
|
After the application starts, navigate to `http://localhost:5000` in your web browser.
|
||||||
|
|
||||||
|
![page](./output.png)
|
||||||
|
|
||||||
|
Stop and remove the containers
|
||||||
|
```
|
||||||
|
$ docker-compose down
|
||||||
|
Stopping server ... done
|
||||||
|
Stopping frontend ... done
|
||||||
|
Stopping mongo ... done
|
||||||
|
Removing server ... done
|
||||||
|
Removing frontend ... done
|
||||||
|
Removing mongo ... done
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Explanation of `docker-compose`
|
||||||
|
|
||||||
|
__Version__
|
||||||
|
|
||||||
|
The first line defines the version of a file. It sounds confusing :confused:. What is meant by version of file ??
|
||||||
|
|
||||||
|
:pill: The Compose file is a YAML file defining services, networks, and volumes for a Docker application. So it is only a version of describing compose.yaml file. There are several versions of the Compose file format – 1, 2, 2.x, and 3.x.
|
||||||
|
|
||||||
|
__Services__
|
||||||
|
|
||||||
|
Our main goal to create a containers, it starts from here. As you can see there are three services(Docker images):
|
||||||
|
- First is __frontend__
|
||||||
|
- Second is __server__ which is __backend - Express(NodeJS)__. I used a name server here, it's totally on you to name it __backend__.
|
||||||
|
- Third is __mongo__ which is db __MongoDB__.
|
||||||
|
|
||||||
|
##### Service app (backend - NodeJS)
|
||||||
|
|
||||||
|
We make image of app from our `DockeFile`, explanation below.
|
||||||
|
|
||||||
|
__Explanation of service server__
|
||||||
|
|
||||||
|
- Defining a **nodejs** service as __server__.
|
||||||
|
- We named our **node server** container service as **server**. Assigning a name to the containers makes it easier to read when there are lot of containers on a machine, it can aslo avoid randomly generated container names. (Although in this case, __container_name__ is also __server__, this is merely personal preference, the name of the service and container do not have to be the same.)
|
||||||
|
- Docker container starts automatically if its fails.
|
||||||
|
- Building the __server__ image using the Dockerfile from the current directory and passing an argument to the
|
||||||
|
backend(server) `DockerFile`.
|
||||||
|
- Mapping the host port to the container port.
|
||||||
|
|
||||||
|
##### Service mongo
|
||||||
|
|
||||||
|
We add another service called **mongo** but this time instead of building it from `DockerFile` we write all the instruction here directly. We simply pull down the standard __mongo image__ from the [DockerHub](https://hub.docker.com/) registry as we have done it for Node image.
|
||||||
|
|
||||||
|
__Explanation of service mongo__
|
||||||
|
|
||||||
|
- Defining a **mongodb** service as __mongo__.
|
||||||
|
- Pulling the mongo 4.2.0 image image again from [DockerHub](https://hub.docker.com/).
|
||||||
|
- Mount our current db directory to container.
|
||||||
|
- For persistent storage, we mount the host directory ( just like I did it in **Node** image inside `DockerFile` to reflect the changes) `/data` ( you need to create a directory in root of your project in order to save changes to locally as well) to the container directory `/data/db`, which was identified as a potential mount point in the `mongo Dockerfile` we saw earlier.
|
||||||
|
- Mounting volumes gives us persistent storage so when starting a new container, Docker Compose will use the volume of any previous containers and copy it to the new container, ensuring that no data is lost.
|
||||||
|
- Finally, we link/depends_on the app container to the mongo container so that the mongo service is reachable from the app service.
|
||||||
|
- In last mapping the host port to the container port.
|
||||||
|
|
||||||
|
:key: `If you wish to check your DB changes on your local machine as well. You should have installed MongoDB locally, otherwise you can't access your mongodb service of container from host machine.`
|
||||||
|
|
||||||
|
:white_check_mark: You should check your __mongo__ version is same as used in image. You can see the version of __mongo__ image in `docker-compose `file, I used __image: mongo:4.2.0__. If your mongo db version on your machine is not same then furst you have to updated your local __mongo__ version in order to works correctly.
|
37
react-express-mongodb/docker-compose.yml
Normal file
37
react-express-mongodb/docker-compose.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: frontend
|
||||||
|
args:
|
||||||
|
FRONT_END_PORT: 5000
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
stdin_open: true
|
||||||
|
volumes:
|
||||||
|
- ./frontend:/usr/src/app
|
||||||
|
- /usr/src/app/node_modules
|
||||||
|
container_name: frontend
|
||||||
|
restart: always
|
||||||
|
server:
|
||||||
|
container_name: server
|
||||||
|
restart: always
|
||||||
|
build:
|
||||||
|
context: server
|
||||||
|
args:
|
||||||
|
NODE_PORT: 3000
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
volumes:
|
||||||
|
- ./server:/usr/src/app
|
||||||
|
- /usr/src/app/node_modules
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
mongo:
|
||||||
|
container_name: mongo
|
||||||
|
restart: always
|
||||||
|
image: mongo:4.2.0
|
||||||
|
volumes:
|
||||||
|
- ./data:/data/db
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
3
react-express-mongodb/frontend/.dockerignore
Normal file
3
react-express-mongodb/frontend/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
server/logs/
|
23
react-express-mongodb/frontend/.gitignore
vendored
Normal file
23
react-express-mongodb/frontend/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
.idea
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
30
react-express-mongodb/frontend/Dockerfile
Normal file
30
react-express-mongodb/frontend/Dockerfile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Create image based on the official Node image from dockerhub
|
||||||
|
FROM node:13.13.0-stretch
|
||||||
|
|
||||||
|
#Argument that is passed from docer-compose.yaml file
|
||||||
|
ARG FRONT_END_PORT
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
#Echo the argument to check passed argument loaded here correctly
|
||||||
|
RUN echo "Argument port is : $FRONT_END_PORT"
|
||||||
|
|
||||||
|
# Copy dependency definitions
|
||||||
|
COPY package.json /usr/src/app
|
||||||
|
COPY package-lock.json /usr/src/app
|
||||||
|
|
||||||
|
# Install dependecies
|
||||||
|
#RUN npm set progress=false \
|
||||||
|
# && npm config set depth 0 \
|
||||||
|
# && npm i install
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Get all the code needed to run the app
|
||||||
|
COPY . /usr/src/app
|
||||||
|
|
||||||
|
# Expose the port the app runs in
|
||||||
|
EXPOSE ${FRONT_END_PORT}
|
||||||
|
|
||||||
|
# Serve the app
|
||||||
|
CMD ["npm", "start"]
|
27
react-express-mongodb/frontend/README.md
Normal file
27
react-express-mongodb/frontend/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#### Snippet of frontend(ReactJS)`DockerFile`
|
||||||
|
|
||||||
|
You will find this `DockerFile` inside **frontend** directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create image based on the official Node image from dockerhub
|
||||||
|
FROM node:10
|
||||||
|
#Argument that is passed from docer-compose.yaml file
|
||||||
|
ARG FRONT_END_PORT
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
#Echo the argument to check passed argument loaded here correctly
|
||||||
|
RUN echo "Argument port is : $FRONT_END_PORT"
|
||||||
|
# Copy dependency definitions
|
||||||
|
COPY package.json /usr/src/app
|
||||||
|
# Install dependecies
|
||||||
|
RUN npm install
|
||||||
|
# Get all the code needed to run the app
|
||||||
|
COPY . /usr/src/app
|
||||||
|
# Expose the port the app runs in
|
||||||
|
EXPOSE ${FRONT_END_PORT}
|
||||||
|
# Serve the app
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
```
|
||||||
|
##### Explanation of frontend(ReactJS) `DockerFile`
|
||||||
|
|
||||||
|
Frontend `DockerFile` is almost the same as Backend `DockerFile`.
|
16121
react-express-mongodb/frontend/package-lock.json
generated
Normal file
16121
react-express-mongodb/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
react-express-mongodb/frontend/package.json
Normal file
40
react-express-mongodb/frontend/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
|
"@testing-library/react": "^9.5.0",
|
||||||
|
"@testing-library/user-event": "^7.2.1",
|
||||||
|
"axios": "^0.19.0",
|
||||||
|
"bootstrap": "^4.3.1",
|
||||||
|
"node-sass": "^4.14.0",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1",
|
||||||
|
"react-scripts": "3.4.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "^2.1.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "PORT=5000 react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "react-app"
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
react-express-mongodb/frontend/public/favicon.ico
Normal file
BIN
react-express-mongodb/frontend/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
43
react-express-mongodb/frontend/public/index.html
Normal file
43
react-express-mongodb/frontend/public/index.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<!--
|
||||||
|
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" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
react-express-mongodb/frontend/public/logo192.png
Normal file
BIN
react-express-mongodb/frontend/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
react-express-mongodb/frontend/public/logo512.png
Normal file
BIN
react-express-mongodb/frontend/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
react-express-mongodb/frontend/public/manifest.json
Normal file
25
react-express-mongodb/frontend/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
3
react-express-mongodb/frontend/public/robots.txt
Normal file
3
react-express-mongodb/frontend/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
56
react-express-mongodb/frontend/src/App.js
vendored
Normal file
56
react-express-mongodb/frontend/src/App.js
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {request} from './utilities/httpRequestHandler'
|
||||||
|
import './App.scss';
|
||||||
|
import AddTodo from "./components/AddTodo";
|
||||||
|
import TodoList from "./components/TodoList";
|
||||||
|
|
||||||
|
export default class App extends React.Component{
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
todos: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
request('get','/api')
|
||||||
|
.then((response) => {
|
||||||
|
this.setState({
|
||||||
|
todos: response.data.data
|
||||||
|
})
|
||||||
|
}).catch((e) => console.log('Error : ', e))
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleAddTodo = (value) => {
|
||||||
|
request('post', '/api/todos', {text:value})
|
||||||
|
.then((response) => {
|
||||||
|
let todosCopy = this.state.todos;
|
||||||
|
todosCopy.unshift({text:value});
|
||||||
|
this.setState({
|
||||||
|
todos : todosCopy
|
||||||
|
});
|
||||||
|
this.refs.todo.value = ""
|
||||||
|
}).catch((e) => console.log('Error : ', e));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="App container">
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-12 col-sm-8 col-md-8 offset-md-2">
|
||||||
|
<h1>Todos</h1>
|
||||||
|
<div className="todo-app">
|
||||||
|
<AddTodo handleAddTodo={(value) => {this._handleAddTodo(value)}}/>
|
||||||
|
<TodoList todos={this.state.todos}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
24
react-express-mongodb/frontend/src/App.scss
Normal file
24
react-express-mongodb/frontend/src/App.scss
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.App {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.todo-app {
|
||||||
|
background-color: #efefef;
|
||||||
|
padding: 1.2em;
|
||||||
|
.new-todo{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
input{
|
||||||
|
width: 80% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//.list-group-item{
|
||||||
|
// &.active:hover{
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// &active:hover{
|
||||||
|
// background-color: #d3d3d3;
|
||||||
|
// }
|
||||||
|
//}
|
9
react-express-mongodb/frontend/src/App.test.js
Normal file
9
react-express-mongodb/frontend/src/App.test.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
ReactDOM.render(<App />, div);
|
||||||
|
ReactDOM.unmountComponentAtNode(div);
|
||||||
|
});
|
22
react-express-mongodb/frontend/src/components/AddTodo.js
vendored
Normal file
22
react-express-mongodb/frontend/src/components/AddTodo.js
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class AddTodo extends React.Component {
|
||||||
|
|
||||||
|
_onAddTodo = () => {
|
||||||
|
if(this.refs.todo.value.length > 0) {
|
||||||
|
this.props.handleAddTodo(this.refs.todo.value);
|
||||||
|
this.refs.todo.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="new-todo form-group">
|
||||||
|
<input type="text" className="form-control" ref="todo"/>
|
||||||
|
<button className="btn btn-primary" onClick={this._onAddTodo}>
|
||||||
|
Add Todo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
48
react-express-mongodb/frontend/src/components/TodoList.js
vendored
Normal file
48
react-express-mongodb/frontend/src/components/TodoList.js
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class TodoList extends React.Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
activeIndex:0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleActive(index) {
|
||||||
|
this.setState({
|
||||||
|
activeIndex: index
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderTodos(todos) {
|
||||||
|
return (
|
||||||
|
<ul className="list-group">
|
||||||
|
{
|
||||||
|
todos.map((todo, i) => {
|
||||||
|
return (<li className={'list-group-item cursor-pointer ' + (i===this.state.activeIndex ? 'active' : '')}
|
||||||
|
key={i}
|
||||||
|
onClick={() => {this._handleActive(i)}}
|
||||||
|
>
|
||||||
|
{todo.text}
|
||||||
|
</li>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { todos } = this.props;
|
||||||
|
return (
|
||||||
|
todos.length > 0 ?
|
||||||
|
this._renderTodos(todos)
|
||||||
|
:
|
||||||
|
<div className="alert alert-primary" role="alert">
|
||||||
|
No Todos to display
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
5
react-express-mongodb/frontend/src/config/constants.js
vendored
Normal file
5
react-express-mongodb/frontend/src/config/constants.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const config = {
|
||||||
|
API_BASE_URL: 'http://localhost:3000',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
8
react-express-mongodb/frontend/src/custom.scss
Normal file
8
react-express-mongodb/frontend/src/custom.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Override default variables before the import
|
||||||
|
$body-bg: #fff;
|
||||||
|
// Import Bootstrap and its default variables
|
||||||
|
@import '~bootstrap/scss/bootstrap.scss';
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
13
react-express-mongodb/frontend/src/index.css
Normal file
13
react-express-mongodb/frontend/src/index.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||||
|
monospace;
|
||||||
|
}
|
14
react-express-mongodb/frontend/src/index.js
vendored
Normal file
14
react-express-mongodb/frontend/src/index.js
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import './index.css';
|
||||||
|
import App from './App';
|
||||||
|
import * as serviceWorker from './serviceWorker';
|
||||||
|
|
||||||
|
import './custom.scss';
|
||||||
|
|
||||||
|
ReactDOM.render(<App />, 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();
|
1
react-express-mongodb/frontend/src/logo.svg
Normal file
1
react-express-mongodb/frontend/src/logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.0 KiB |
135
react-express-mongodb/frontend/src/serviceWorker.js
vendored
Normal file
135
react-express-mongodb/frontend/src/serviceWorker.js
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// This optional code is used to register a service worker.
|
||||||
|
// register() is not called by default.
|
||||||
|
|
||||||
|
// This lets the app load faster on subsequent visits in production, and gives
|
||||||
|
// it offline capabilities. However, it also means that developers (and users)
|
||||||
|
// will only see deployed updates on subsequent visits to a page, after all the
|
||||||
|
// existing tabs open on the page have been closed, since previously cached
|
||||||
|
// resources are updated in the background.
|
||||||
|
|
||||||
|
// To learn more about the benefits of this model and instructions on how to
|
||||||
|
// opt-in, read https://bit.ly/CRA-PWA
|
||||||
|
|
||||||
|
const isLocalhost = Boolean(
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
// [::1] is the IPv6 localhost address.
|
||||||
|
window.location.hostname === '[::1]' ||
|
||||||
|
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||||
|
window.location.hostname.match(
|
||||||
|
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export function register(config) {
|
||||||
|
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||||
|
// The URL constructor is available in all browsers that support SW.
|
||||||
|
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||||
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
|
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||||
|
// from what our page is served on. This might happen if a CDN is used to
|
||||||
|
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||||
|
|
||||||
|
if (isLocalhost) {
|
||||||
|
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||||
|
checkValidServiceWorker(swUrl, config);
|
||||||
|
|
||||||
|
// Add some additional logging to localhost, pointing developers to the
|
||||||
|
// service worker/PWA documentation.
|
||||||
|
navigator.serviceWorker.ready.then(() => {
|
||||||
|
console.log(
|
||||||
|
'This web app is being served cache-first by a service ' +
|
||||||
|
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Is not localhost. Just register service worker
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerValidSW(swUrl, config) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register(swUrl)
|
||||||
|
.then(registration => {
|
||||||
|
registration.onupdatefound = () => {
|
||||||
|
const installingWorker = registration.installing;
|
||||||
|
if (installingWorker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
installingWorker.onstatechange = () => {
|
||||||
|
if (installingWorker.state === 'installed') {
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// At this point, the updated precached content has been fetched,
|
||||||
|
// but the previous service worker will still serve the older
|
||||||
|
// content until all client tabs are closed.
|
||||||
|
console.log(
|
||||||
|
'New content is available and will be used when all ' +
|
||||||
|
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onUpdate) {
|
||||||
|
config.onUpdate(registration);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// At this point, everything has been precached.
|
||||||
|
// It's the perfect time to display a
|
||||||
|
// "Content is cached for offline use." message.
|
||||||
|
console.log('Content is cached for offline use.');
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onSuccess) {
|
||||||
|
config.onSuccess(registration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error during service worker registration:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkValidServiceWorker(swUrl, config) {
|
||||||
|
// Check if the service worker can be found. If it can't reload the page.
|
||||||
|
fetch(swUrl)
|
||||||
|
.then(response => {
|
||||||
|
// Ensure service worker exists, and that we really are getting a JS file.
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (
|
||||||
|
response.status === 404 ||
|
||||||
|
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||||
|
) {
|
||||||
|
// No service worker found. Probably a different app. Reload the page.
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.unregister().then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Service worker found. Proceed as normal.
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log(
|
||||||
|
'No internet connection found. App is running in offline mode.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregister() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.unregister();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
22
react-express-mongodb/frontend/src/utilities/httpRequestHandler.js
vendored
Normal file
22
react-express-mongodb/frontend/src/utilities/httpRequestHandler.js
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import config from '../config/constants';
|
||||||
|
|
||||||
|
|
||||||
|
export async function request (method, uri, data, headers = null, params = null) {
|
||||||
|
let url = (config.API_BASE_URL + uri);
|
||||||
|
let query = {
|
||||||
|
method: method,
|
||||||
|
url: url
|
||||||
|
};
|
||||||
|
if (headers !== null)
|
||||||
|
query.headers = headers;
|
||||||
|
if (params !== null)
|
||||||
|
query.params = params;
|
||||||
|
if (method === 'post' || method === 'put' || method === 'delete' || method === 'patch')
|
||||||
|
query.data = data;
|
||||||
|
try {
|
||||||
|
return await axios(query);
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
BIN
react-express-mongodb/output.png
Normal file
BIN
react-express-mongodb/output.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
27
react-express-mongodb/server/Dockerfile
Normal file
27
react-express-mongodb/server/Dockerfile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
FROM node:13.13.0-stretch-slim
|
||||||
|
|
||||||
|
#Argument that is passed from docer-compose.yaml file
|
||||||
|
ARG NODE_PORT
|
||||||
|
|
||||||
|
#Echo the argument to check passed argument loaded here correctly
|
||||||
|
RUN echo "Argument port is : $NODE_PORT"
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
#COPY . .
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Install app dependencies
|
||||||
|
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||||
|
# where available (npm@5+)
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
|
||||||
|
#In my case my app binds to port NODE_PORT so you'll use the EXPOSE instruction to have it mapped by the docker daemon:
|
||||||
|
|
||||||
|
EXPOSE ${NODE_PORT}
|
||||||
|
|
||||||
|
CMD npm run dev
|
||||||
|
|
||||||
|
|
41
react-express-mongodb/server/README.md
Normal file
41
react-express-mongodb/server/README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#### Snippet of backend(Node.js)`DockerFile`
|
||||||
|
|
||||||
|
You will find this `DockerFile` file in the root directory of the project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FROM node:13.13.0-stretch-slim
|
||||||
|
#Argument that is passed from docer-compose.yaml file
|
||||||
|
ARG NODE_PORT
|
||||||
|
#Echo the argument to check passed argument loaded here correctly
|
||||||
|
RUN echo "Argument port is : $NODE_PORT"
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
#COPY . .
|
||||||
|
COPY . .
|
||||||
|
# Install app dependencies
|
||||||
|
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||||
|
# where available (npm@5+)
|
||||||
|
RUN npm install
|
||||||
|
#In my case my app binds to port NODE_PORT so you'll use the EXPOSE instruction to have it mapped by the docker daemon:
|
||||||
|
EXPOSE ${NODE_PORT}
|
||||||
|
CMD npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Explanation of backend(Node.js) `DockerFile`
|
||||||
|
|
||||||
|
- The first line tells Docker to use another Node image from the [DockerHub](https://hub.docker.com/). We’re using the official Docker image for Node.js and it’s version 10 image.
|
||||||
|
|
||||||
|
- On second line we declare argument `NODE_PORT` which we will pass it from `docker-compose`.
|
||||||
|
|
||||||
|
- On third line we log to check argument is successfully read
|
||||||
|
|
||||||
|
- On fourth line we sets a working directory from where the app code will live inside the Docker container.
|
||||||
|
|
||||||
|
- On fifth line, we are copying/bundling our code working directory into container working directory on line three.
|
||||||
|
|
||||||
|
- On line seven, we run npm install for dependencies in container on line four.
|
||||||
|
|
||||||
|
- On Line eight, we setup the port, that Docker will expose when the container is running. In our case it is the port which we define inside `.env` file, read it from `docker-compose` then passed as a argument to the (backend)`DockerFile`.
|
||||||
|
|
||||||
|
- And in last, we tell docker to execute our app inside the container by using node to run `npm run dev. It is the command which I registered in __package.json__ in script section.
|
||||||
|
###### :clipboard: `Note: For development purpose I used __nodemon__ , If you need to deploy at production you should change CMD from __npm run dev__ to __npm start__.`
|
12
react-express-mongodb/server/config/config.js
vendored
Normal file
12
react-express-mongodb/server/config/config.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
var env = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
if(env === 'development' || env === 'test'){
|
||||||
|
|
||||||
|
const config = require('./config.json')
|
||||||
|
let envConfig = config[env];
|
||||||
|
console.log(envConfig);
|
||||||
|
|
||||||
|
Object.keys(envConfig).forEach((key) => {
|
||||||
|
process.env[key] = envConfig[key]
|
||||||
|
})
|
||||||
|
}
|
10
react-express-mongodb/server/config/config.json
Normal file
10
react-express-mongodb/server/config/config.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"test":{
|
||||||
|
"PORT": 3000,
|
||||||
|
"MONGODB_URI": "mongodb://mongo:27017/TodoAppTest"
|
||||||
|
},
|
||||||
|
"development":{
|
||||||
|
"PORT": 3000,
|
||||||
|
"MONGODB_URI": "mongodb://mongo:27017/TodoApp"
|
||||||
|
}
|
||||||
|
}
|
62
react-express-mongodb/server/config/messages.js
vendored
Normal file
62
react-express-mongodb/server/config/messages.js
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
module.exports = {
|
||||||
|
AUTHENTICATION_FAILED: {
|
||||||
|
code: 400,
|
||||||
|
message: 'Authentication failed. Please login with valid credentials.',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
SUCCESSFUL_LOGIN: {
|
||||||
|
code: 200,
|
||||||
|
message: 'Successfully logged in',
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
INTERNAL_SERVER_ERROR: {
|
||||||
|
code: 500,
|
||||||
|
message: 'Something unexpected happened',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
UNAUTHORIZED: {
|
||||||
|
code: 401,
|
||||||
|
message: 'You session is expired. Please login again',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
SUCCESSFUL_DELETE: {
|
||||||
|
code: 200,
|
||||||
|
message: 'Successfully deleted',
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
SUCCESSFUL_UPDATE: {
|
||||||
|
code: 200,
|
||||||
|
message: 'Updated successfully',
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
SUCCESSFUL: {
|
||||||
|
code: 200,
|
||||||
|
success: true,
|
||||||
|
message: 'Successfully completed',
|
||||||
|
},
|
||||||
|
NOT_FOUND: {
|
||||||
|
code: 404,
|
||||||
|
success: true,
|
||||||
|
message: 'Requested API not found',
|
||||||
|
},
|
||||||
|
ALREADY_EXIST: {
|
||||||
|
code: 200,
|
||||||
|
success: true,
|
||||||
|
message: 'Already exists',
|
||||||
|
},
|
||||||
|
FORBIDDEN: {
|
||||||
|
code: 403,
|
||||||
|
message: 'You are not authorized to complete this action',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
BAD_REQUEST: {
|
||||||
|
code: 400,
|
||||||
|
message: 'Bad request. Please try again with valid parameters',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
IN_COMPLETE_REQUEST: {
|
||||||
|
code: 422,
|
||||||
|
message: 'Required parameter missing',
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
};
|
30
react-express-mongodb/server/db/index.js
vendored
Normal file
30
react-express-mongodb/server/db/index.js
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Created by Syed Afzal
|
||||||
|
*/
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const {log} = require('../utils/helpers/logger');
|
||||||
|
|
||||||
|
exports.connect = (app) => {
|
||||||
|
const options = {
|
||||||
|
useNewUrlParser: true,
|
||||||
|
autoIndex: false, // Don't build indexes
|
||||||
|
reconnectTries: 30, // Retry up to 30 times
|
||||||
|
reconnectInterval: 500, // Reconnect every 500ms
|
||||||
|
poolSize: 10, // Maintain up to 10 socket connections
|
||||||
|
// If not connected, return errors immediately rather than waiting for reconnect
|
||||||
|
bufferMaxEntries: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectWithRetry = () => {
|
||||||
|
mongoose.Promise = global.Promise;
|
||||||
|
console.log('MongoDB connection with retry')
|
||||||
|
mongoose.connect(process.env.MONGODB_URI, options).then(()=>{
|
||||||
|
console.log('MongoDB is connected');
|
||||||
|
app.emit('ready');
|
||||||
|
}).catch(err=>{
|
||||||
|
console.log('MongoDB connection unsuccessful, retry after 2 seconds.')
|
||||||
|
setTimeout(connectWithRetry, 2000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
connectWithRetry();
|
||||||
|
};
|
BIN
react-express-mongodb/server/logs/project.log
Normal file
BIN
react-express-mongodb/server/logs/project.log
Normal file
Binary file not shown.
14
react-express-mongodb/server/models/todos/todo.model.js
Normal file
14
react-express-mongodb/server/models/todos/todo.model.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Created by Syed Afzal
|
||||||
|
*/
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
var Todo = mongoose.model('Todo', {
|
||||||
|
text : {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {Todo};
|
1605
react-express-mongodb/server/package-lock.json
generated
Normal file
1605
react-express-mongodb/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
react-express-mongodb/server/package.json
Normal file
27
react-express-mongodb/server/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "docker_node_mongo_starter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "docker starter with node js and mongodb services",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js",
|
||||||
|
"dev": "nodemon server.js"
|
||||||
|
},
|
||||||
|
"author": "Syed Afzal",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"body-parser": "^1.18.2",
|
||||||
|
"cookie-parser": "^1.4.4",
|
||||||
|
"cors": "^2.8.4",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"lodash": "^4.17.13",
|
||||||
|
"mongodb": "^3.0.7",
|
||||||
|
"mongoose": "^5.0.15",
|
||||||
|
"simple-node-logger": "^18.12.23",
|
||||||
|
"validator": "^10.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.3"
|
||||||
|
}
|
||||||
|
}
|
38
react-express-mongodb/server/routes/index.js
vendored
Normal file
38
react-express-mongodb/server/routes/index.js
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const serverResponses = require('../utils/helpers/server.responses');
|
||||||
|
const messages = require('../config/messages');
|
||||||
|
var {Todo} = require('../models/todos/todo.model');
|
||||||
|
|
||||||
|
const routes = (app) => {
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post('/todos', (req,res)=>{
|
||||||
|
var todo = new Todo({
|
||||||
|
text: req.body.text
|
||||||
|
});
|
||||||
|
|
||||||
|
todo.save()
|
||||||
|
.then((result)=>{
|
||||||
|
serverResponses.sendSuccess(res,messages.SUCCESSFUL, result);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
serverResponses.sendError(res,messages.BAD_REQUEST,e)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/', (req,res) => {
|
||||||
|
Todo.find({}, {__v:0})
|
||||||
|
.then((todos)=>{
|
||||||
|
serverResponses.sendSuccess(res,messages.SUCCESSFUL, todos);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
serverResponses.sendError(res,messages.BAD_REQUEST,e)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
//it's a prefix before api it is useful when you have many modules and you want to
|
||||||
|
//differentiate b/w each module you can use this technique
|
||||||
|
app.use('/api', router);
|
||||||
|
};
|
||||||
|
module.exports = routes;
|
35
react-express-mongodb/server/server.js
vendored
Normal file
35
react-express-mongodb/server/server.js
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Created by Syed Afzal
|
||||||
|
*/
|
||||||
|
require('./config/config');
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const path = require('path');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const cors = require('cors');
|
||||||
|
const db = require('./db');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
//connection from db here
|
||||||
|
db.connect(app);
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
// adding routes
|
||||||
|
require('./routes')(app);
|
||||||
|
|
||||||
|
app.on('ready', () => {
|
||||||
|
app.listen(3000, () => {
|
||||||
|
console.log("Server is up on port", 3000)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = app;
|
9
react-express-mongodb/server/utils/helpers/logger.js
vendored
Normal file
9
react-express-mongodb/server/utils/helpers/logger.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const filename = path.join(__dirname, '../../logs/project.log');
|
||||||
|
|
||||||
|
//you can change format according to you
|
||||||
|
const log = require('simple-node-logger').createSimpleLogger( {
|
||||||
|
logFilePath:filename,
|
||||||
|
timestampFormat:'YYYY-MM-DD HH:mm:ss'}
|
||||||
|
);
|
||||||
|
module.exports = {log};
|
@ -0,0 +1,21 @@
|
|||||||
|
const serverResponse = {
|
||||||
|
sendSuccess: (res, message, data = null) => {
|
||||||
|
const responseMessage = {
|
||||||
|
code: message.code ? message.code : 500,
|
||||||
|
success: message.success,
|
||||||
|
message: message.message,
|
||||||
|
};
|
||||||
|
if (data) { responseMessage.data = data; }
|
||||||
|
return res.status(message.code).json(responseMessage);
|
||||||
|
},
|
||||||
|
sendError: (res, error) => {
|
||||||
|
const responseMessage = {
|
||||||
|
code: error.code ? error.code : 500,
|
||||||
|
success: false,
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
return res.status(error.code ? error.code : 500).json(responseMessage);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = serverResponse;
|
Loading…
Reference in New Issue
Block a user