Search code examples
node.jsmongodbexpressejsmulter

"image.forEach is not a function" even though it is?


I've been having this weird problem with EJS recently. I'm trying to make a simple homebrew web image server, and everything is going well. I am able to upload and store the image, but I can't retrieve it.

I'm getting a "image.forEach" is not a function error, even though that I set to be a function.

Here is back-end:

//Routes
app.get('/', (req, res)=> {
    imageUpload.find({}, (err, data)=> {
        if(err) {
            console.log(err)
            res.status(500)
        } else {
            console.log('> ' + req.ip + " connected to: " + req.url)
            res.render('index.ejs', {
                title: 'Home :: Image Server',
                version: settings.VERSION,
                image: data
            })
        }
    })
})

app.post('/upload', upload.single('img'), (req, res)=> {
    const newImageUpload = new imageUpload({
        img: {
            data: fs.readFileSync(path.join(__dirname + '/uploads/' + req.file.filename)),
            contentType: 'image/png'
        }
    })

    newImageUpload.save((err, data)=> {
        if(err) {
            console.log(err)
            res.status(500)
        } else {
            console.log('> Image succesfully uploaded.')
            res.redirect('/')
        }
    })
})

app.get("/images/:id", (req, res)=> {
    const id = req.params.id

    imageUpload.findById(id, (err, data)=> {
        if(err) {
            console.log(err)
        } else {
            console.log('> ' + req.ip + " connected to: " + req.url)
            res.render('image.ejs', {
                title: 'Home :: Image Server',
                image: data
            })
        }
    })
})

I'm able to retrieve the image to the index via '/' route with no problems, but I get the error on the '/images/:id' route.

here is the 'image.ejs' file:

<!DOCTYPE html>
<html>
    <head>
        <%= title %> 
    </head>
    <body>
        <% image.forEach((image)=> { %>
            <div>
                <img src="data:image/<%=image.img.contentType%>;base64, <%=image.img.data.toString('base64')%>">
            </div>
        <% }) %> 
    </body>
</html>

I tried using async and await, but the route doesn't load. If anyone could help me, that would be greatly appreciated!


Solution

  • Your problem is the image in image.forEach is supposed to be an array. When you .find in your / route it works, because .find sends in an array of data to it's callback function. In your /images/:id route on the other hand your .findById function is instead returning an image object instead of an array (this is expected when you are trying to find something by a unique identifier it should should only return one thing). If you want to keep the single ejs file for both routes this can be remedied either by checking in your ejs whether it's an array (using something like an if statement in ejs and an Array.isArray(image) check) or just always sending an array even if there is only one object. Of course if you want to use seperate ejs files you can always do @MohamedOraby's answer for the /images/:id route and your previous one for your / route.

    For example if you wanted to always send the array and keep the ejs as is you could do this to you /images/:id route:

    app.get("/images/:id", (req, res)=> {
        const id = req.params.id
    
        imageUpload.findById(id, (err, data)=> {
            if(err) {
                console.log(err)
            } else {
                console.log('> ' + req.ip + " connected to: " + req.url)
                res.render('image.ejs', {
                    title: 'Home :: Image Server',
                    image: [data] // this line changed from `image: data` to `image: [data]`
                })
            }
        })
    })
    
    

    Feel free to ask me questions of clarifications though I may not respond immediately.