Router cannot be exported when using es6 syntax

48 Views Asked by At

I am trying to rewrite NodeJS modules(constant xxx = require('yyy')) into es6 format. I tried to export the router using export syntax in es6 but the following error always popped up:

      throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
            ^

TypeError: Router.use() requires a middleware function but got a Module
    at Function.use (C:\Users\blog\node_modules\express\lib\router\index.js:469:13)
    at Function.<anonymous> (C:\Users\blog\node_modules\express\lib\application.js:227:21)
    at Array.forEach (<anonymous>)
    at Function.use (C:\Users\blog\node_modules\express\lib\application.js:224:7)
    at file:///C:/Users/blog/server.js:20:5
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

I am sure that my es6 syntax should be correct:

export default router;

The code may be related to this error is as below:

route/article.js

import express from 'express'
import * as Article from './../models/article.js';
const router = express.Router()

router.get('/new', (req, res) => {
  res.render('article/new', { article: new Article() })
})

router.get('/edit/:id', async (req, res) => {
  const article = await Article.findById(req.params.id)
  res.render('article/edit', { article: article })
})

router.get('/:slug', async (req, res) => {
  const article = await Article.findOne({ slug: req.params.slug })
  if (article == null) res.redirect('/')
  res.render('article/show', { article: article })
})

router.post('/', async (req, res, next) => {
  req.article = new Article()
  next()
}, saveArticleAndRedirect('new'))

router.put('/:id', async (req, res, next) => {
  req.article = await Article.findById(req.params.id)
  next()
}, saveArticleAndRedirect('edit'))

router.delete('/:id', async (req, res) => {
  await Article.findByIdAndDelete(req.params.id)
  res.redirect('/')
})

function saveArticleAndRedirect(path) {
  return async (req, res) => {
    let article = req.article
    article.title = req.body.title
    article.description = req.body.description
    article.markdown = req.body.markdown
    try {
      article = await article.save()
      res.redirect(`/article/${article.slug}`)
    } catch (e) {
      res.render(`article/${path}`, { article: article })
    }
  }
}

export default router;

Other code that may be related:

models/article.js

import mongoose from 'mongoose'
import {marked} from 'marked'
import slugify from 'slugify'
import createDomPurify from 'dompurify'
import { JSDOM }  from 'jsdom'
const dompurify = createDomPurify(new JSDOM().window)

const articleSchema = new mongoose.Schema({
 title:{
    type: String,
    required: true
 },
 description: {
    type: String
 },
 markdown: {
    type: String,
    required: true
 },
 createdAt: {
    type: Date,
    default: Date.now
 },
 slug: {
   type: String,
   required: true,
   unique: true
 },
 sanitizedHTML: {
   type: String, 
   required: true
 }
})

articleSchema.pre('validate', function(next) {
   if(this.title) {
      this.slug = slugify(this.title, {lower: true, strict:true})
   }
   if(this.markdown){
    
      this.sanitizedHTML = DomPurify.sanitize(marked.parse(this.markdown))
         
      }
      else{
         console.log("Error")
      }
   next()
})

export const Article = mongoose.model('Article', articleSchema);

server.js:

import express from 'express'
import mongoose from 'mongoose'
import * as Article from './models/article.js';
import * as articleRouter from './route/article.js';
import methodOverride from 'method-override'
const app = express()


mongoose.connect('mongodb://localhost:27017/', {})

app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(methodOverride('_method'))

app.get('/', async (req, res) => {
  const article = await Article.find().sort({ createdAt: 'desc' })
  res.render('article/index', { article: article })
})

app.use('/article', articleRouter)

app.listen(3000)

The file structure is as below:

enter image description here

================================================

Update

Thank you for Mike's suggestion, but still, another error popped up:

file:///C:/Users/blog/server.js:16
  const article = await Article.find().sort({ createdAt: 'desc' })
                                ^

TypeError: Article.find is not a function
    at file:///C:/Users/blog/server.js:16:33
    at Layer.handle [as handle_request] (C:\Users\blog\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Users\blog\node_modules\express\lib\router\route.js:149:13)
    at Route.dispatch (C:\Users\blog\node_modules\express\lib\router\route.js:119:3)
    at Layer.handle [as handle_request] (C:\Users\blog\node_modules\express\lib\router\layer.js:95:5)
    at C:\Users\blog\node_modules\express\lib\router\index.js:284:15
    at Function.process_params (C:\Users\blog\node_modules\express\lib\router\index.js:346:12)
    at next (C:\Users\blog\node_modules\express\lib\router\index.js:280:10)
    at methodOverride (C:\Users\blog\node_modules\method-override\index.js:65:14)
    at Layer.handle [as handle_request] (C:\Users\blog\node_modules\express\lib\router\layer.js:95:5)
1

There are 1 best solutions below

3
Mike K On BEST ANSWER

The problem is with your import * as articleRouter from './route/article.js';.

Your article.js file exports nothing other than a default export (router). This is why you're catching this error.

You could also, instead, try use-ing articleRouter.default, if you want to keep import * as..., but that's obviously a bad practice.

import express from 'express'
import mongoose from 'mongoose'
import * as Article from './models/article.js';
import articleRouter from './route/article.js';
import methodOverride from 'method-override'
const app = express()


mongoose.connect('mongodb://localhost:27017/', {})

app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(methodOverride('_method'))

app.get('/', async (req, res) => {
  const article = await Article.find().sort({ createdAt: 'desc' })
  res.render('article/index', { article: article })
})

app.use('/article', articleRouter)

app.listen(3000)