Coder Social home page Coder Social logo

jonathanwexler / get-programming-with-nodejs Goto Github PK

View Code? Open in Web Editor NEW
184.0 12.0 152.0 2.13 MB

Code samples for Get Programming with Node.js (See verhagen's VM setup for exercises in this book: https://github.com/verhagen/get-programming-with-nodejs)

JavaScript 59.15% HTML 0.74% CSS 6.54% EJS 33.58%
nodejs javascript npm web development

get-programming-with-nodejs's Introduction


NOTICE: As of 11/20/20, The following packages have been upgraded due to dependency vulnerabilities:

mongodb --> 3.6.3 bcrypt --> 5.0.0

NOTICE: As of 1/20/20, The following packages have been upgraded due to dependency vulnerabilities:

mongoose --> 5.8.9 mongodb --> 3.5.2 bcrypt --> 3.0.0 node-pre-gyp lodash


Code Readme

Outline of projects

unit 0

Lesson 2

unit 1

Lesson 3

Lesson 4

Lesson 5

Lesson 6

Lesson 7

unit 2

Lesson 8

Lesson 9

Lesson 10

Lesson 11

Lesson 12

unit 3

Lesson 13

Lesson 14

Lesson 15

Lesson 16

unit 4

Lesson 17

Lesson 18

Lesson 19

Lesson 20

Lesson 21

unit 5

Lesson 22

Lesson 23

Lesson 24

Lesson 25

unit 6

Lesson 26

Lesson 27

Lesson 28

Lesson 29

unit 7

Lesson 30

Lesson 31

Lesson 32

Lesson 33

unit 8

Lesson 34

Lesson 35

Lesson 36

Lesson 37

get-programming-with-nodejs's People

Contributors

gustavo71 avatar jasonsalas avatar jonathanwexler avatar splowman398 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

get-programming-with-nodejs's Issues

Add course.IsJoined Property in Other method (Lesson 27)

The book suggests using middle-ware to add a isJoind property to courses. We can here move some code to course instance methods. We add an instance method to course which receives a users and then return a true if that user joined that course and return false in other cases. The code below added to Course Schema:

courseSchema.methods.isUserJoined=function(user) {
    const course=this;
    if (!user) {return false;}
    return user.courses.indexOf(course._id.toString())>-1;
}

Then we add it to the middle-ware as follows:

res.locals.courses=res.locals.courses
        .map(course=>
            Object.assign(course.toObject(),
            {isJoined:course.isUserJoined(req.user)})
        );

As the book suggests here we should use toObject to first convert mongoose document to POCO.

Express-Validator Middlewares: A clean and similar approach

Unfortunately the express validator mechanism has changed and the book solution doesn't work because in new versions, every validator is a middle-ware so you can use them directly in route definitions or as I do, use the following approach.
1- Define Validation Rules almost similar to past in an array. I do this in user controller and I import express validator first:
`

const expressValidator=require("express-validator");
const validationsRules=[
    expressValidator.sanitizeBody("email")
    .normalizeEmail({
        all_lowercase:true
    }).trim(),
    expressValidator.body("email","Email is Invalid").isEmail(),
    expressValidator.body("zipCode","Zip Code is Invalid")
    .isInt().notEmpty().isLength({min:5,max:5}),
    expressValidator.body("password","Password Is Required")
    .notEmpty()
];

`
2- Due to asynchronous nature of middle-wars you can define and use one of the following middle-wares (both worked true in my case!)


validateAsync:async(req,res,next)=>{
        await Promise.all(validationsRules.map(v=>v.run(req)));
        const err=expressValidator.validationResult(req);
        if (err.isEmpty()) {
            next();
        } else {
            const message=err.array().map(e=>e.msg).join(" and ");
            req.flash("error",message);
            res.redirect("/users/new");
        }

    },
    validate:(req,res,next)=>{
        Promise.all(validationsRules.map(v=>v.run(req)))
        .then((result)=>{
            const err=expressValidator.validationResult(req);
            if (err.isEmpty()) {
                next();
            } else {
                const message=err.array().map(e=>e.msg).join(" and ");
                req.flash("error",message);
                res.redirect("/users/new");
            }
        })
        .catch(err=>{
            next(err);
        });
        
    },

Can't log in if the email contains a dot

If you sign up with an email with a dot (eg [email protected]), the dot is removed in usersController.validate before being saved to the database.

If you later try to log in with that email with a dot, since the email does not get sanitized in usersController.validate and thus the dot is not removed, the input email and the database email don't match, so you can't log in. The error flash message just says "Failed to login.".

If you add some code in usersController.authenticate you can see that the info param in passport.authenticate callback is "[IncorrectUsernameError]: Password or username is incorrect".

One option to fix this is to run the express-validator code in /users/login like is done in /users/create:

router.post(
  '/users/login',
  usersController.validate, // <-- add this
  usersController.authenticate
)

Using JWT to Authneticate Socket.IO An Have current user in Our codes

In this method for authentication I send my requests with an extra headers as follows:

$.post("/_api/socket/login",(data)=>{
        console.log(data);
        let apiToken=data.token;
        const socket=io({extraHeaders:{token:apiToken},auth:{token:apiToken}});
    });

With the first post request we get a token (It is done in express so we easily can get a token in the case of user is authenticated) and then I initialize my socket with an extraHeaders (I can't get auth in express middlewares! Altough I think it dont different because of nature of tokens). Then I add the following middlewares in my io Initialization in server:

socketGetToken:(req,res,next)=>{
        if (req.isAuthenticated()) {
            let user=req.user;
            if (user) {
                let signedToken=JWT.sign({
                    data:user._id,
                    exp:(new Date()).setMinutes((new Date()).getMinutes()+5)
                },jwtSecret);
                res.status(statusCodes.OK);
                return res.json({
                    status:statusCodes.OK,
                    success:true,
                    token:signedToken
                })
            } else {
                res.status(statusCodes.UNAUTHORIZED);
                return res.json({
                    status:statusCodes.UNAUTHORIZED,
                    success:false,
                    message:"Couldn't Authenticate User!"
                });
                
            }

        }
        passport.authenticate("local",(err,user)=>{
            if (user) {
                let signedToken=JWT.sign({
                    data:user._id,
                    exp:(new Date()).setDate((new Date()).getDate()+1)
                },jwtSecret);
                res.status(statusCodes.OK);
                return res.json({
                    status:statusCodes.OK,
                    success:true,
                    token:signedToken
                })
            } else {
                res.status(statusCodes.UNAUTHORIZED);
                return res.json({
                    status:statusCodes.UNAUTHORIZED,
                    success:false,
                    message:"Couldn't Authenticate User!"
                });
                
            }
        })(req,res,next);
    },

  verifyJWTSocket:(req,res,next)=>{
    console.log(req)
    
    let jwt=req.headers.token;
    if (!jwt) {
      next(new Error("UnAuthorized!"));
    }
    JWT.verify(jwt,jwtSecret,(err,payload)=>{
      if (!payload) {
        next(new Error("UnAuthorized!"));
      }
      User.findById(payload.data)
      .then(user=>{
        // req.logIn(user,err=>{});//configure current user for using in API for example enrolment
        req.user=user; //This is better in case of no session or cookie!
        next();
      })
      .catch(err=>{
        next(new Error(err.message));
      })
    })

  },

const wrapper=middleware=>(client,next)=>middleware(client.request,{},next);

module.exports=(io)=>{
    io.use(wrapper(userController.verifyJWTSocket));
    io.use((client,next)=>{
        if (client.request.user) {
            
            next();
        }
        else {
            next(new Error("UnAuthorized!"));
        }
    });
    io.on("connection",client=>{
        console.log(client.request.user)
        console.log("Socket Connected");
    });
}

As you can see we have current user too according the requestee of token and we can be assure that no one can don't fake itself instead of Others! (which is simply possible using hidden fields in html!)

Tiresome Console Error from Express?

Any suggestions?

Trying to work through the lessons, but keep getting this console error that wastes 1.5 hours per lesson. Am 99% certain it comes from Express. Don't know what causes the error because it comes and goes inexplicably. Source code doesn't seem to be the problem. The requested page always renders perfectly, the data set logs in terminal, then the verbose terminal error logs. Here are 4 things I've done to "fix" the problem:

  1. "Fixed" code that essentially changed nothing (readability, ES6 semantics, etc.). Re-started node and problem disappeared.
  2. Removed the code (still) in lesson 17, main.js @line 42. That call, res.render("subscribers", { subscribers: req.data });, seems to be redundant to the same call in getAllSubscribers.
  3. Last night, ran rm -rf node_modules then npm i. Problem disappeared, again no code changes.
  4. Today the error reappeared. I usually launch directly to http://localhost:3000/subscribers. So I went from there to a couple other routes. Went back to subscribers and the console was clean. Stopped node, re-started node main.js or npm start, it doesn't matter. Went to http://localhost:3000/subscribers - errors re-appear.

Not fun. This is the error copied from my terminal:
`ERROT state: Error: Failed to lookup view "404 | The page does not exist!" in views directory "/home/myHome/develop/recipe_app/views"
at Function.render (/home/myHome/develop/recipe_app/node_modules/express/lib/application.js:580:17)
at ServerResponse.render (/home/myHome/develop/recipe_app/node_modules/express/lib/response.js:1012:7)
at ServerResponse.res.render (/home/myHome/develop/recipe_app/node_modules/express-ejs-layouts/lib/express-layouts.js:77:18)
at exports.respondNoResourceFound (/home/myHome/develop/recipe_app/controllers/errorController.js:19:8)
at Layer.handle [as handle_request] (/home/myHome/develop/recipe_app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:317:13)
at /home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:335:12)
at next (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:275:10)
at Layer.handle [as handle_request] (/home/myHome/develop/recipe_app/node_modules/express/lib/router/layer.js:91:12)
at trim_prefix (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:317:13)
at /home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:335:12)
at next (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:275:10)
at jsonParser (/home/myHome/develop/recipe_app/node_modules/body-parser/lib/types/json.js:110:7)
at Layer.handle [as handle_request] (/home/myHome/develop/recipe_app/node_modules/express/lib/router/layer.js:95:5)

TypeError [ERR_INVALID_ARG_VALUE]: The argument 'id' must be a non-empty string. Received ''
at Module.require (internal/modules/cjs/loader.js:1037:11)
at require (internal/modules/cjs/helpers.js:77:18)
at new View (/home/myHome/develop/recipe_app/node_modules/express/lib/view.js:81:14)
at Function.render (/home/myHome/develop/recipe_app/node_modules/express/lib/application.js:570:12)
at ServerResponse.render (/home/myHome/develop/recipe_app/node_modules/express/lib/response.js:1012:7)
at ServerResponse.res.render (/home/myHome/develop/recipe_app/node_modules/express-ejs-layouts/lib/express-layouts.js:77:18)
at exports.respondInternalError (/home/myHome/develop/recipe_app/controllers/errorController.js:29:8)
at Layer.handle_error (/home/myHome/develop/recipe_app/node_modules/express/lib/router/layer.js:71:5)
at trim_prefix (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:315:13)
at /home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:335:12)
at next (/home/myHome/develop/recipe_app/node_modules/express/lib/router/index.js:275:10)
at /home/myHome/develop/recipe_app/node_modules/express-ejs-layouts/lib/express-layouts.js:80:40
at Function.render (/home/myHome/develop/recipe_app/node_modules/express/lib/application.js:582:14)
at ServerResponse.render (/home/myHome/develop/recipe_app/node_modules/express/lib/response.js:1012:7)
at ServerResponse.res.render (/home/myHome/develop/recipe_app/node_modules/express-ejs-layouts/lib/express-layouts.js:77:18)
`

Authentication in Socket.io and getting currentUser (Lesson 32)

As you see the socket.io in server doesnt know sessions and it doesn't place in the middleware chains so you can get them authenticated or not which is not very secure. For this reason, there is methods and tools to authenticated them.
One way is using passport as socket.io suggestes. But I try different methods including manual authentication using call back but I always get error that there is no credentials. But after some search I find methods which the following method is simple. It needs a package named passport.socketio. Its idea is to share session used in express with socket. so If you authenticated in express you authenticated at socket and vice versa.
They suggest not to use memory store for session but as we use it for learning I found a method to use it. First I create an store using the following code:
var sessionStore= new MemoryStore();
Then I introduce it to express-session. (I declare it in main.js but you can declare them in separate module and then export and use them!)

app.use(expressSession({
    store:sessionStore,
    secret:app.get("secret"),
    cookie:{
        maxAge:4000000
    },
saveUninitialized:false,
resave:false
}));

Then I use the package in chatController in socket initialization function as a socket middlerware

    io.use(passportSocketIo.authorize({
        cookieParser: cookieParser,       // the same middleware you registrer in express
        key:          'connect.sid',       // the name of the cookie where express/connect stores its session_id
        secret:       secret,    // the session_secret to parse the cookie
        store:        sessionStore       // we NEED to use a sessionstore. no memorystore please
        // success:      onAuthorizeSuccess,  // *optional* callback on success - read more below
        // fail:         onAuthorizeFail,     // *optional* callback on fail/error - read more below
      }));

    io.use((socket, next) => {
        if (socket.request.isAuthenticated()) {
            next();
        } else {
            next(new Error("unauthorized"))
        }
    });

for using same session I define same session.id, same store and same secret as you see.
Then everything is authenticated as they say!

MongoLab doesnt provide service for Heroku

Unfortunately with Heroku I can not setup my mongo db service from MongoLab because They dont provide service for Heroku. There is not no free other services there. Altough Microsoft Azure provide similar services which can be search easily from internet.

I just ended the development phase of project and I am really and absolutely greatefull for this comprehensive book. It covers (Altough abstract) all the matter related to backend development including routing/ database/api/authentication/cookie and session/ static and dynamic html files/ work with files and streams/ web socket/ json web token/ cookie based Auth and importantly MVC structure in Express JS which is really valuable.
Thanks again

Lesson 28: Using JWT for geting/joining/leaving course in browser

Because JWT signing middle-ware is post and needs email and password it may seems hard to use JWT in client side. The point is in changing that middle-war for Authenticated User.
We first change that middle-ware as follows:

apiAuthenticate:(req,res,next)=>{
        if (req.isAuthenticated()) {
            let user=req.user;
            if (user) {
                let signedToken=JWT.sign({
                    data:user._id,
                    exp:(new Date()).setMinutes((new Date()).getMinutes()+5)
                },jwtSecret);
                res.status(statusCodes.OK);
                return res.json({
                    status:statusCodes.OK,
                    success:true,
                    token:signedToken
                })
            } else {
                res.status(statusCodes.UNAUTHORIZED);
                return res.json({
                    status:statusCodes.UNAUTHORIZED,
                    success:false,
                    message:"Couldn't Authenticate User!"
                });
                
            }

        }
        passport.authenticate("local",(err,user)=>{
            if (user) {
                let signedToken=JWT.sign({
                    data:user._id,
                    exp:(new Date()).setDate((new Date()).getDate()+1)
                },jwtSecret);
                res.status(statusCodes.OK);
                return res.json({
                    status:statusCodes.OK,
                    success:true,
                    token:signedToken
                })
            } else {
                res.status(statusCodes.UNAUTHORIZED);
                return res.json({
                    status:statusCodes.UNAUTHORIZED,
                    success:false,
                    message:"Couldn't Authenticate User!"
                });
                
            }
        })(req,res,next);
    }

Then this api return a token to browser too. we can save the generated token in browser and use it second times or you can request it every time need it (which needs more resource in server!) or even you can use it in session too. By the way I choose to request it with every web request I send. So I change my Ajax codes as follow:


$(function() {
    $("#modal-button").on('click',function() {
        // let apiToken=0;
        // if ($("#apiToken").length) {
        //     apiToken=$("#apiToken").val();
        // }
        
        // $("#myModal .modal-body").load("/courses?format=json");
        $("#myModal .modal-body").html('');
        $.post("/_api/login",(data)=>{
            let apiToken=data.token;
            $.ajax({
                url:`/_api/courses`,
                headers:{token:apiToken},
                success:(result)=>{
                    result.data.forEach(course => {
                        $("#myModal .modal-body").append(
                            `<div class="course">
                                <span class="course-title">
                                    ${course.title}
                                </span>
    
                                <button class="${'button'+" "+(course.isJoined?'joined-button':'join-button')}" data-id="${course._id}" >${course.isJoined?"Leave":"Join"}</button>
                                <div class="course-description">
                                    ${course.description}
                                </div>
                           
                            </div>
                            `
                        );
                    });
                    addJoinButtonListenerDynamic(apiToken);
                },
                error:(response,status,xhr)=>{console.log(status);}
            
            });
    
        });
    });
    
})

//this dynamicly set event when needed and can do without the content of modal
let addJoinButtonListenerDynamic=(apiToken)=>{
    // let apiToken=0;
    //     if ($("#apiToken").length) {
    //         apiToken=$("#apiToken").val();
    //     }
    $("#myModal").on("click","button.join-button",function(event) {
        // let $button=$(event.target);
        let $button=$(this);
        let courseId=$button.data("id");
        $.ajax({
            url:`/_api/courses/${courseId}/join`,
            headers:{token:apiToken},
            success:data=>{
                if (data.success) {
                    $button.text("Joined")
                    .addClass("joined-button")
                    .removeClass("join-button");
                } else {
                    $button.text("Try Again");
                }
            },
            error:(response,status,xhr)=>{console.log(status);}
        });
    });
    $("#myModal").on("click","button.joined-button",function(event) {
        // let $button=$(event.target);
        let $button=$(this);
        let courseId=$button.data("id");

        $.ajax({
            url:`/_api/courses/${courseId}/leave`,
            headers:{token:apiToken},
            success:data=>{
                if (data.success) {
                    $button.text("Join")
                    .removeClass("joined-button")
                    .addClass("join-button");
                } else {
                    $button.text("Try Again");
                }
            },
            error:(response,status,xhr)=>{console.log(status);}
        });
    });
}

MongoDB 7.0.7 does not accept callback function for API functions

According to the MongoDB/Mongoose docs, the usage of callback functions has been deprecated in Mongoose 7. x. Therefore, the sample code needs to be updated to comply with the changes especially in lesson_13, lesson_14, and possibly lesson_15, if callback functions is still used in the API calls.

Join To Course Issue:After Joining class Event should removed! (Lesson 27)

After you join the courses event listener that added for the button remain unchanged which make errors if you try to click on them!
But if use the dynamic binding of event in JQuery this problem doesnt raise.
consider the original course:

let addJoinButtonListener=()=>{
    $(".join-button").click(event => {
        let $button = $(event.target),
          courseId = $button.data("id");
          debugger;
        $.get(`/_api/courses/${courseId}/join`, (data) => {
          if (data.success) {
            $button
              .text("Joined")
              .addClass("joined-button")
              .removeClass("join-button");
          } else {
            $button.text("Try again");
          }
        });
      });
}

The first problem here is that the button should be added to content (As mentioned in the book). But the main problem is that this event remain intact after joining to course.
consider the improved version of could below:

//this dynamicly set event when needed and can do without the content of modal
let addJoinButtonListenerDynamic=()=>{
    $("#myModal").on("click","button.join-button",function(event) {
        // let $button=$(event.target);
        let $button=$(this);
        let courseId=$button.data("id");
        debugger;
        $.get(`/_api/courses/${courseId}/join`,data=>{
            if (data.success) {
                $button.text("Joined")
                .addClass("joined-button")
                .removeClass("join-button");
            } else {
                $button.text("Try Again");
            }
        });
    });
}

first this add everywhere in the code even during page load! secondly and mainly after removing the join-button the event doesn't fire if we click at the button because there is only one event an it is on Modal and dynamicly decide to work on every click if it was join-button!

Passport Local strategy from ground up (Lesson I learned and it is just for sharing)

Here I implemented the passport local strategy independent of mongoose database which is helpful for implementing your authentication method. we need four functions. The rest done by Passport itself
1-verifyCallback: it is a function which receive three arguments (username,password,done)
2-serializeCallback:it is a function which receive two arguments (user,done)
3-deserializeCallback:it is a function which receive two arguments (userId,done)
4-createStrategy:this function create an strategy to use by passport.
Here I implemented very basic of this fours:

const LocalStrategy=require("passport-local").Strategy;
const verifyCallback=(username,password,done)=>{
    //Here I only return the case of authenticating for implementing a
    //true logic you can first check user and password combination to be true 
    //according to saved data in database. in case of success you should return 
    //done(null,returnedUser). In case of mismatch return done(null,false,{message:""})
    //and in case of Error in process return done(error); in other two case
    // the first argument is null
    return done(null,{id:1,username:"ardeshir",age:"33",fullname:"Ashraf Hakimi"});
}
const createStrategy=()=>new LocalStrategy({
    usernameField:"username",
    passwordField:"password"
},verifyCallback);

const serializeCallBack=(user,done)=>{
    //you can serialize every field of user which you like
    //normally user_id or username or email which is unique to find user
    //quickly in deserialize callback
    return done(null,user);
}
const desrializeCallBack=(userId,done)=>{
    //Here you can implement a logic to return full user data from user id and then return it 
    //instead of userId itself!
    return done(null,userId);
}

module.exports={createStrategy,serializeCallBack,desrializeCallBack}

then you need to configure passport to use this 4 functions:

passport.use(customeAuthModule.createStrategy());
passport.serializeUser(customeAuthModule.serializeCallBack);
passport.deserializeUser(customeAuthModule.desrializeCallBack);

Then we need to introduce passport to express:


app.use(passport.initialize());
app.use(passport.session()); 

And then define the routing middlewares as followes:

const customAuthRouter=express.Router();
customAuthRouter.get("/login",(req,res,next)=>{res.render("custome-auth/custome-login");});
customAuthRouter.post("/login",passport.authenticate("local",{
    successRedirect:"/",failureRedirect:"/custome-auth/login",successFlash:"Successfull",failureFlash:true
}));

module.exports=customAuthRouter;

and use this router in your main app. You can design your login form whatever you like including username and password inputs!

Pre Save Hook doesnt fire on updating password which is really unexpected!

In Unit 5 lesson 23 You presented the pre("save") hook for Hashing password which unfortunately doesn't work in Update case as I find out after a thorough search and attempts. If users try to edit their password it saves unhashed in database!
Similar approach proposed by many people in the internet which all rely in the assumption that this hook fire before update too which is not the case. I really disappointed from MongoDB and Mongoose!
Similar conversation:
Github
https://stackoverflow.com/questions/14588032/mongoose-password-hashing
I change my code in order to updating works as expected

updateUser:(req,res,next)=>{
        let userId=req.params.id;
        User.findById(userId)
        .then(user=>{
            user.name={
                    first:req.body.first,
                    last:req.body.last
                };
            user.email=req.body.email;
            user.password=req.body.password;
            user.zipCode=req.body.zipCode;
            return user.save();
        })
        .then(u=>{
            res.locals.user=u;
            next();
        })
        .catch(err=>{
            res.locals.errorMessage=err.message;
            res.render("error");
        });

findByIdAndUpdate

There is a parameter named new in findByIdAndUpdate method which change the returned value in then part of Method. Al tough we don't encounter this in the exact copy of your code but it needs attention. consider the following case with small change from lesson 21:

Course.findByIdAndUpdate(courseId, {
      $set: courseParams
    },{new:true})
      .then(course => {
        res.locals.course = course;
        res.render("courses/show");
      })
      .catch(error => {
        console.log(`Error updating course by ID: ${error.message}`);
        next(error);
      });

Here if you use not the new:true the redirected view (with direct res.render and not with res.redirect) doesnt show the edited version of course!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.