NodeJS - Express, Mongo, Sequelize, Sequelize-CLI, Mongoose, GraphQL, Websockets, Socket.io, REST APIs, ReactJS Social Network, Winston, Morgan, Helmet, Compression, Validators, SSL/TLS, Micro Services Architecture & lot more. My personal notes and apps.
Aditya Hajare (Linkedin).
WIP (Work In Progress)!
- Weather App: https://aditya-hajare-weather-app.herokuapp.com
- Tasks Manager App: https://aditya-hajare-nodejs-task-app.herokuapp.com
- Socket.io Chat App: https://aditya-hajare-socket-chat-app.herokuapp.com
Open-sourced software licensed under the MIT license.
- Options Request
- Debugging Using Node Debugger
- Call Stack
- Event Loop
- Deploying Weather App On Heroku
- JEST - Things To Know
- WebSockets Protocol
- Mongoose - Things To Know
- Cookies
- Sessions
- Storing Sessions In MongoDB
- Allow CORS For REST APIs
- GraphQL
- GraphQL Query Variables
- Browser sends a
OPTIONS
request before it sendsPOST, PATCH, PUT, DELETE
etc.. requests. - You may typically get
405 (Method Not Allowed)
error. Express GraphQL
automatically declines anything which is not aPOST
orGET
request. So theOPTIONS
request is denied.- To fix this, install
cors
bynpm i cors --save
and use it as below:const cors = require('cors'); const app = express(); app.options('*', cors()); app.use(cors());
- Add
debugger
keyword wherever you want to stop your program execution and begin debugging. For e.g.://app.js console.log('Hello World'); debugger; // This is where program execution will stop and you can start debugging. console.log('Hello World 1');
- Run
app.js
above withinspect
command as below:// In terminal node inspect app.js
- Open
Google Chrome Browser
and enter following URL:chrome://inspect/#devices
- You should see your current Node app under
Remote Target
. Click oninspect
link. - On left hand side, click on
Add folder to workspace
.
Call Stack
is a simple data structure provided by theV8 JavaScript Engine
.- It's job is to track the execution of our program and it does that by keeping track of all of the functions that are currently running.
- The
Call Stack
data structure usesFILO (First In Last Out)
to track the execution of our program.
Event Loop
looks at 2 things:- It looks at the
Call Stack
. - And it looks at the
Callback Queue
.
- It looks at the
- If the
Call Stack
is empty, it's going the run the items fromCallback Queue
. - The
Event Loop
actually have to wait untilCall Stack
is empty before it could run items fromCallback Queue
. - None of our
Asynchronous Functions
are going to run unlessmain() Function
is done executing. - Node uses other threads (
C++
) behind the scene forNode APIs
.
- Go to local Git repository root directory or project root directory.
- Execute following command:
heroku create [SUB_DOMAIN] // For e.g. heroku create aditya-hajare-weather-app
- NOTE:
aditya-hajare-weather-app
is the sub-domain and it must be unique acrossHeroku
. - From your project root, execute following command:
heroku git:remote -a [APP_NAME] // For e.g. heroku git:remote -a aditya-hajare-weather-app
- Execute
git remote
to list remote branches. You should see something like below:heroku origin
- NOTE:
heroku
is listed underremotes
. - To Deploy:
- To deploy master branch to Heroku:
git push heroku master
- To deploy contents of specific directory to Heroku root:
git subtree push --prefix [DIRECTORY_NAME] heroku master // For e.g. git subtree push --prefix 10-Weather-App-Express heroku master
- To deploy master branch to Heroku:
- After deployment, you can visit your app in browser. For e.g.
// Weather App on Heroku: https://aditya-hajare-weather-app.herokuapp.com/
- Heroku Environment Variables:
- To set environment variable in Heroku environment:
heroku config:set KEY=VALUE
- To unset/remove environment variable in Heroku environment:
heroku config:unset KEY
- To set environment variable in Heroku environment:
.toBe()
uses===
operator to compare values. i.e.1 === 1
: True{} === {}
: False. That is because when 2 objects are compared with===
they are not equal as they are stored in different memory locations.
- To compare objects in JEST, use
.toEqual()
.
WebSocket
is a separate protocol fromHTTP
.WebSockets
allow forFull Duplex Communication
.Full Duplex Communication
is just a fancy term forBi-Directional Communication
.- With
WebSockets
we have aPersistent Connection
between client and server. Socket.io
needs to be called with a rawHTTP Server
.- Event emitters:
socket.emit('EVENT_NAME', { dataObject })
: Only to self/specific client.socket.broadcast.emit('EVENT_NAME', { dataObject })
: All other clients except self.io.emit('EVENT_NAME', { dataObject })
: All clients including self.
- Event emitters in
Rooms
:io.to(room).emit('EVENT_NAME', { dataObject })
: Emits an events to everybody in a specific room.socket.broadcast.to(room).emit('EVENT_NAME', { dataObject })
: Emits an events to everybody in a specific room except self/specific client.
- While defining methods on
Schema
, avoidArrow Functions
. Instead opt out forfunction()
.- Reason:
this
is not available inArrow Functions
. - Refer to following examples:
17-Shop-App-Mongoose/models/user.js 11-Tasks-Manager-App/src/models/user.js 11-Tasks-Manager-App/src/models/task.js
- For e.g.
const mongoose = require('mongoose'); const userSchema = mongoose.Schema({ name: { type: String, required: true }, email: { type: String, required: true } }); userSchema.methods.helloWorld = function() { // Not using arrow function here! // Code }; module.exports = mongoose.model('User', userSchema);
- Reason:
- Cookies which are not having
expiry
andmax-age
set, will get destroyed when browser is closed. - To set a cookie:
exports.postLogin = async (req, res, next) => { res.setHeader('Set-Cookie', 'isAuthenticated=true; HttpOnly'); res.send(req.body); }
- To fetch value of
isAuthenticated
cookie:const value = req.get('Cookie') .split(';')[1] .trim() .split('=')[1];
- To use sessions in
Express
, install following plugin:npm i express-session --save
- Steps to initialize and use session:
- Initilize session in middleware in
app.js
:const session = require('session'); const app = express(); app.use(session({ secret: 'some secret string', resave: false, saveUninitialized: false }));
- Session will be available on
req.session
. - To set a value in session:
req.session.isLoggedIn = true;
- To fetch a value from session:
const loggedIn = req.session.isLoggedIn;
- Initilize session in middleware in
- Install following package:
npm i connect-mongodb-session --save
- Setup
MongoDB Store
:const session = require('session'); const connectMongoDBSession = require('connect-mongodb-session'); const MongoDBStore = connectMongoDBSession(session); const store = new MongoDBStore(); app.use(session({ secret: 'some secret string', resave: false, saveUninitialized: false, store }));
- Method #1:
- Using express
cors
middleware package:- Install
cors
npm package:npm i cors --save
- Use it in
app.js
:const express = require('express'); const cors = require('cors'); // CORS const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.use(cors()); // CORS app.listen(8080);
- Install
- Using express
- Method #2:
- Manually setting headers (NOTE: May not work in most cases):
const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.use((req, res, next) => { // CORS Middleware res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); next(); }) app.listen(8080);
- Manually setting headers (NOTE: May not work in most cases):
- Uses
Typed Query Language
. - Single
POST
request endpoint. For e.g.POST /graphql
POST Request Body
containsQuery Expression (to define the Data that should be returned)
.- Operation Types:
Query
: To retrieve data.Mutation
: To manipulate data.Subscription
: To set up realtime connection viaWebsockets
.
- Example #1
- Query without using variables (Non-Recommended Way):
const graphqlQuery = { query: ` { posts(page: ${page}) { posts { id title content imageUrl creator { name email } createdAt updatedAt } totalPosts } } ` }
- Query using explicit variables (Recommended Way):
const graphqlQuery = { query: ` query FetchPosts($page: Int) { posts(page: $page) { posts { id title content imageUrl creator { name email } createdAt updatedAt } totalPosts } } `, variables: { page } };
- Query without using variables (Non-Recommended Way):
- Example #2
- Query without using variables (Non-Recommended Way):
const graphqlQuery = { query: ` mutation { updateStatus(status: "${this.state.status}") { status } } ` };
- Query using explicit variables (Recommended Way):
const graphqlQuery = { query: ` mutation UpdateUserStatus ($userStatus: String) { updateStatus(status: $userStatus) { status } } `, variables: { userStatus: this.state.status } };
- Query without using variables (Non-Recommended Way):