node.jsexpresslokijs

Node-LokiJS: not all get requests are fulfilled, request status is forever pending?


I followed this tutorial on file upload with multer-express and then retrieving image by id Scoth.io.

The setup uses LokiJS as database.

All the API's works fine however when API to retrieve multiple images by ID isn't working for multiple images. Most of get requests are pending on server (status is pending in Network panel of Chrome Dev tools).

The requests never gets completed once in pending state (forever pending) and server gets stuck in that.

Pending-network-requests-chrome-dev-tools

API to Retrieve Image by Id

app.get('/images/:id', async (req, res) => {
    try {
        const col = await loadCollection(COLLECTION_NAME, db);
        const result = col.get(req.params.id);

        if (!result) {
            res.sendStatus(404);
            return;
        };

        res.setHeader('Content-Type', result.mimetype);
        fs.createReadStream(path.join(UPLOAD_PATH, result.filename)).pipe(res);
    } catch (err) {
        res.sendStatus(400);
    }
})

Index.ts (All API's)

import * as express from 'express'
import * as multer from 'multer'
import * as cors from 'cors'
import * as fs from 'fs'
import * as path from 'path'
import * as Loki from 'lokijs'

import { loadCollection, imageFilter } from './utils'

//setup
  const DB_NAME = 'db.json'
  const COLLECTION_NAME = 'images'
  const UPLOAD_PATH = 'uploads'
  const upload = multer({ dest: `${UPLOAD_PATH}/`, fileFilter: imageFilter }) //MULTER CONFIG
  const db = new Loki(`${UPLOAD_PATH}/${DB_NAME}`, { persistenceMethod: 'fs' })

// app  
  const app = express();
  app.use(cors());

  app.get('/', (req, res) => {
    res.json({responseText : 'Server running successfully'})
  })


  //Upload Single
  app.post('/fileUpload', upload.single('file'), async (req, res) => {

    try {
        const col = await loadCollection(COLLECTION_NAME, db)
        const data = col.insert(req.file)

        db.saveDatabase()
        res.send({id: data.$loki, fileName: data.filename, originalName: data.originalname })
      } catch (err) {
        res.sendStatus(400)
      }
  })

//Upload Multiple
 app.post('/photos/upload', upload.array('photos', 12), async (req, res) => {

    try {
        const col = await loadCollection(COLLECTION_NAME, db)
        const data = [].concat(col.insert(req.files))

        db.saveDatabase()
        res.send(data.map(x => ({ id: x.$loki, fileName: x.filename, originalName: x.originalname })));
    } catch (err) {
        res.sendStatus(400)
      }
  })

 //Retrieve Image
 app.get('/images', async (req, res) => {
   try {
       const col = await loadCollection(COLLECTION_NAME, db)
       res.send(col.data)
   } catch(err) {
     res.sendStatus(400)
   }
 })


// Retrieve Image by Id
 app.get('/images/:id', async (req, res) => {
   try {
       
       const col = await loadCollection(COLLECTION_NAME, db)
       const result = col.get(parseInt(req.params.id))

       if(!result) {
         res.sendStatus(404)
         return;
       }

     res.setHeader('Content-Type', result.mimetype);
     fs.createReadStream(path.join(UPLOAD_PATH, result.filename)).pipe(res)
   } catch(err) {
     res.sendStatus(400)
   }
 })

app.listen(3000, function () {
    console.log('listening on port 3000!');
})

Index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Fetch Image (NodeJS-LokiJS API) - Example</title>
    <style>
      .photo {
        width: 100px;
        display: block;
        margin-bottom: 5px;
        border: 2px solid black;
      }
    </style>    
  </head>
  <body>
    <h1>HTML Example</h1>
    <p>
      JPEG:<br>
      <img class="photo" src="http://localhost:3000/images/1">
      <img class="photo" src="http://localhost:3000/images/2">
      <img class="photo" src="http://localhost:3000/images/3">
      <img class="photo" src="http://localhost:3000/images/4">
      <img class="photo" src="http://localhost:3000/images/5">
      <img class="photo" src="http://localhost:3000/images/6">
    </p>
  </body>
</html>

utils.ts

import * as del from 'del'
import * as Loki from 'lokijs'

const loadCollection = function (colName, db: Loki): Promise<Loki.Collection<any>> {
    return new Promise(resolve => {
    db.loadDatabase({}, _=> {
      const _collection = db.getCollection(colName) || db.addCollection(colName)
      resolve(_collection)
    })
  })
}

const imageFilter = function (req, file, cb) {
    // accept image only
    if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
        return cb(new Error('Only image files are allowed!'), false);
    }
    cb(null, true);
}

export { imageFilter, loadCollection }


Solution

  • Looks like there is a problem with loadCollection indeed. Remove db.loadDatabase from loadCollection and load your db only once, in index.ts after you initialised it.

    index.ts

    import * as express from 'express'
    import * as multer from 'multer'
    import * as cors from 'cors'
    import * as fs from 'fs'
    import * as path from 'path'
    import * as Loki from 'lokijs'
    
    import { loadCollection, imageFilter } from './utils'
    
    //setup
      const DB_NAME = 'db.json'
      const COLLECTION_NAME = 'images'
      const UPLOAD_PATH = 'uploads'
      const upload = multer({ dest: `${UPLOAD_PATH}/`, fileFilter: imageFilter }) //MULTER CONFIG
      const db = new Loki(`${UPLOAD_PATH}/${DB_NAME}`, { persistenceMethod: 'fs' })
      db.loadDatabase({});
    
    // app  
      const app = express();
      app.use(cors());
    
      app.get('/', (req, res) => {
        res.json({responseText : 'Server running successfully'})
      })
    
    
      //Upload Single
      app.post('/fileUpload', upload.single('file'), async (req, res) => {
    
        try {
            const col = await loadCollection(COLLECTION_NAME, db)
            const data = col.insert(req.file)
    
            db.saveDatabase()
            res.send({id: data.$loki, fileName: data.filename, originalName: data.originalname })
          } catch (err) {
            res.sendStatus(400)
          }
      })
    
    //Upload Multiple
     app.post('/photos/upload', upload.array('photos', 12), async (req, res) => {
    
        try {
            const col = await loadCollection(COLLECTION_NAME, db)
            const data = [].concat(col.insert(req.files))
    
            db.saveDatabase()
            res.send(data.map(x => ({ id: x.$loki, fileName: x.filename, originalName: x.originalname })));
        } catch (err) {
            res.sendStatus(400)
          }
      })
    
     //Retrieve Image
     app.get('/images', async (req, res) => {
       try {
           const col = await loadCollection(COLLECTION_NAME, db)
           res.send(col.data)
       } catch(err) {
         res.sendStatus(400)
       }
     })
    
    
    // Retrieve Image by Id
     app.get('/images/:id', async (req, res) => {
       try {
    
           const col = await loadCollection(COLLECTION_NAME, db)
           const result = col.get(parseInt(req.params.id))
    
           if(!result) {
             res.sendStatus(404)
             return;
           }
    
         res.setHeader('Content-Type', result.mimetype);
         fs.createReadStream(path.join(UPLOAD_PATH, result.filename)).pipe(res)
       } catch(err) {
         res.sendStatus(400)
       }
     })
    
    app.listen(3000, function () {
        console.log('listening on port 3000!');
    })
    
    

    utils.ts

    import * as del from 'del'
    import * as Loki from 'lokijs'
    
    const loadCollection = function (colName, db: Loki): Promise<Loki.Collection<any>> {
        return new Promise(resolve => {
          const _collection = db.getCollection(colName) || db.addCollection(colName)
          resolve(_collection)
      })
    }
    
    const imageFilter = function (req, file, cb) {
        // accept image only
        if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
            return cb(new Error('Only image files are allowed!'), false);
        }
        cb(null, true);
    }
    
    export { imageFilter, loadCollection }