Search code examples
node.jsmongodbmongoosekoanunjucks

KOA-Views Suddenly Stop Working after First Route


The full code for this project can be found here: https://github.com/AlexMercedCoder/KoaStarterBlog

The Video series I was making with this can be found here so you can see how things worked at different stages: https://www.youtube.com/watch?v=8_aWw7lfKKI&list=PLY6oTPmKnKbbF4t0Y9DcUVYi7f4kix7Qj

I actually illustrate the problem in the beginning part of this video and walk through the construction of all my routes: https://youtu.be/ltAxokJsaWE

So I built out this basic blog app using KoaJS. When I initially run the index.js the behavior is as follows. - Root Route Works - Create route works - Admin route works - The Delete Button works on the admin page - The edit route just doesn't not (works on and off even though the code is just like the other routes)

Bigger Problem: After submitting a form by hitting the delete button or by creating a new post all the routes except the create route stop working and instead they just return (not found). At first I thought this was a problem being caused by ctx.redirect because they would always fail and be followed by the broken routes but while rendering a complete page seems to work initially typing root or admin route into the browser after form submission still breaks.

*Update: this happens after going to any route, every route works if its the first route accessed but then all other routes except create stop working afterwards. It's as if the first route creates some sort of limbo. The weird thing is the router still console logs everything the route should do up until either a ctx.render, ctx.redirect or ctx.body is to be returned.

below is the index.js code!

///////////////////////
//Initializing Environment Variables and other middleware
//npm i dotenv
//npm i koa-methodoverride
///////////////////////
require('dotenv').config();
const override = require('koa-methodoverride');
const parser = require('koa-bodyparser');


////////////////////////
//Connecting the DB
//npm i mongoose
////////////////////////
const mongoose = require('mongoose');
const db = mongoose.connection;
const host = process.env.host;
const dbupdate = {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false};
mongoose.connect(host, dbupdate);

db.on('error', (err) => console.log('Error, DB Not connected'));
db.on('connected', () => console.log ('connected to mongo'));
db.on('diconnected', () => console.log ('Mongo is disconnected'));
db.on('open', () =>console.log ('Connection Made!'));

////////////////////////////
//Model Schema
///////////////////////////
const Blog = require('./model/blog.js');


///////////////////////
//Create Our Server Object
//npm i koa
///////////////////////
const koa = require('koa');
const server = new koa();

//////////////////////////
//Create Our Static Folder
//npm i koa-static
//////////////////////////
const static = require('koa-static');

//////////////////////////
//Creating Our Router
//npm i koa-router
//////////////////////////
const Router = require('koa-router');
const route = new Router();

/////////////////////////////////
//initializing views
//npm i koa-views
//npm i nunjucks
////////////////////////////////;
const views = require('koa-views');
const nunj = require('nunjucks');
nunj.configure('./views', {autoescape: true});


///////////////////////////
//routes
// route.get - route.post - route.patch - post.put - route.delete
///////////////////////////

//root route
route.get('/', (ctx, next) => {
    console.log('connected to root route');
    return Blog.find({}, (error, results) => {
        console.log(results)
        ctx.render('index.njk', {
            posts: results
        });
    });
});

//admin route
route.get('/admin', (ctx, next) => {
    console.log('connected to admin route');
    return Blog.find({}, (error, results) => {
        console.log(results)
        ctx.render('admin.njk', {
            posts: results
        });
    });
});

//delete route
route.delete('/delete/:id', (ctx, next) => {
    console.log('connected to delete route');
    console.log(ctx.request.body)
    if (ctx.request.body.pw === process.env.pw){
        Blog.findByIdAndRemove(ctx.params.id, (err, result) => {

       })
    }else{
        console.log('wrong password')

    }
    return ctx.render('complete.njk');
});

//edit route
route.get('/edit/:id', (ctx, next) => {
    console.log('connected to edit route');
    return Blog.findById(ctx.params.id, (err, results) => {
        console.log(results);
        ctx.render('edit.njk', {
        post: results
        });
    });
});

route.put('/edit/:id', (ctx, next) => {
    console.log('editing a post');
    console.log(ctx.request.body)
    if (ctx.request.body.pw === process.env.pw){
        Blog.findByIdAndUpdate(ctx.params.id, ctx.request.body, {new:True}, (err, result) => {
         console.log(result); 
        })
     }else{
         console.log('wrong password');
        }
    return ctx.render('complete.njk');
});

//create route
route.get('/create', (ctx, next) => {
    console.log('connected to create route');
    return ctx.render('create.njk');
});

route.post('/create', (ctx, next) => {
    console.log('creating a post');
    console.log(ctx.request.body)
    if (ctx.request.body.pw === process.env.pw){
        Blog.create(ctx.request.body, (err, result) => {
         console.log(result); 
        })
     }else{
         console.log('wrong password');
        ;
     }
     return ctx.render('complete.njk');
});

////////////////////////////
//Async Functions
////////////////////////////
// const getPosts = async (query) => { 
//     const data = await Blog.find({query}) 
//     return data; 
//   }; 

////////////////////////
//Middleware
/////////////////////////
server.use(parser());
server.use(override('_method'))
server.use(views('./views', {map: {njk: 'nunjucks'}}));
server.use(route.routes());
server.use(static('./public'));




/////////////////////
//Our Listener on Port 1985
/////////////////////
server.listen(1985,'localhost',() => console.log('Listening on port 1985'));

Solution

  • It looks like a http-server fault. Try to add an error handler.

    Also I recomment to change error handling in code e.g.

    route.put('/edit/:id', (ctx, next) => {
        if (ctx.request.body.pw === process.env.pw){
            Blog.findByIdAndUpdate(ctx.params.id, ctx.request.body, {new:True}, (err, result) => {
             console.log(result); 
            })
         } else {
             console.log('wrong password');
            }
        return ctx.render('complete.njk');
    });
    

    replace by

    route.put('/edit/:id', (ctx, next) => {
      // Early stop to avoid brace ladder
      if (ctx.request.body.pw != process.env.pw)
        ctx.throw('Env. wrong password'); // Pass request to error-handler
    
      Blog.findByIdAndUpdate(ctx.params.id, ctx.request.body, {new:True}, (err, result) => {
        if (err)              
          ctx.throw('Db wrong password'); // or throw Error('Db wrong password');
    
        ctx.render('complete.njk');
      });
    }
    ...
    server.use(route.routes());
    server.use(static('./public'));
    
    // Error handler: catch 'wrong password'-error here.
    app.on('error', (err, ctx) => {
      console.error(err);
      ctx.render('error.njk'); 
    });
    

    P.S. I use Express, not Koa. So maybe I made some mistakes.


    Perhaps ctx.render requires async-await framing like below

    route.get('/', async (ctx, next) => {
        console.log('connected to root route');
        console.log(ctx);
        return Blog.find({}, (error, results) => {
            console.log(results)
            await ctx.render('index', {
                posts: results
            });
            console.log('the view was rendered')
        });
    });
    

    I downloaded your git and made this changes. And it works :)