hoangvvo / next-connect

The TypeScript-ready, minimal router and middleware layer for Next.js, Micro, Vercel, or Node.js http/http2

Home Page:https://www.npmjs.com/package/next-connect

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

types error after updating to v1.0.0-next.2

HT-Moh opened this issue · comments

Trying to update the nextjs upload API after updating next-connect to the last version but getting two types errors:

First type error

Type 'NextApiRequest' is missing the following properties from type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>': get, header, accepts, acceptsCharsets, and 23 more.ts(2345)

Second type error:

 Types of parameters 'request' and 'args_0' are incompatible.
      Property 'file' is missing in type 'NextApiRequest' but required in type 'NextApiRequestExtended'.ts(2

Code before the update

import * as mime from 'mime-types'
import multer, { MulterError } from 'multer'
import { NextApiRequest, NextApiResponse } from 'next'
import nextConnect from 'next-connect'

import withAuth, { HASURA_ROLE } from '@/middlewares/withAuth'

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
}

interface NextApiRequestExtended extends NextApiRequest {
  file: any
}
const upload = multer({
  storage: multer.diskStorage({
    destination: (request, file, callback) => {
      const destinationPath =
        request.query.isProjectPic === 'true'
          ? `./public/uploads/project`
          : `./public/uploads/profile`
      callback(null, destinationPath)
    },
    filename: (request, file, callback) => {
      const extension = mime.extension(file.mimetype)
      if (typeof extension === `string`) {
        const fileNames = file.originalname.split('.', 1)
        const { userId } = request.query
        if (fileNames.length > 0) {
          const fileName = fileNames[0]
            .toLowerCase()
            .replace(/\s/g, '-')
            // eslint-disable-next-line unicorn/prefer-spread
            .concat('-', userId ? (userId as string) : '', '.', extension)
          callback(null, fileName)
          return
        }
      }
      callback(null, file.originalname)
    },
  }),
  limits: { fileSize: 2 * 1024 * 1024 },
  fileFilter: (request, file, callback) => {
    if (
      file.mimetype === 'image/png' ||
      file.mimetype === 'image/jpg' ||
      file.mimetype === 'image/jpeg'
    ) {
      callback(null, true)
    } else {
      return callback(new Error('Invalid mime type'))
    }
  },
})

const apiRoute = nextConnect({
  onError(error, request: NextApiRequest, response: NextApiResponse) {
    return response.status(501).json(error)
  },
  onNoMatch(request, response) {
    return response
      .status(405)
      .json({ error: `Method '${request.method}' Not Allowed` })
  },
})

apiRoute.use(upload.single('image-upload'))

apiRoute.post(async (request: NextApiRequestExtended, response) => {
  try {
    if (!request.file) {
      response.statusCode = 400
      return response.status(404).end('File was not provided')
    }
    const file = request.file
    response.status(200).send({
      filename: file.filename,
      size: file.size,
      path: file.path,
    })
  } catch (error: any) {
    return response.status(404).end((error as MulterError).message)
  }
})

export default withAuth(apiRoute, HASURA_ROLE.USER)

code after update

/* eslint-disable unicorn/no-null */
import * as mime from 'mime-types'
import multer, { MulterError } from 'multer'
import { NextApiRequest, NextApiResponse } from 'next'
import { createRouter } from 'next-connect'

import withAuth, { HASURA_ROLE } from '@/middlewares/withAuth'

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
}

interface NextApiRequestExtended extends NextApiRequest {
  file: any
}

const upload = multer({
  storage: multer.diskStorage({
    destination: (request, file, callback) => {
      const destinationPath =
        request.query.isProjectPic === 'true'
          ? `./public/uploads/project`
          : `./public/uploads/profile`
      callback(null, destinationPath)
    },
    filename: (request, file, callback) => {
      const extension = mime.extension(file.mimetype)
      if (typeof extension === `string`) {
        const fileNames = file.originalname.split('.', 1)
        const { userId } = request.query
        if (fileNames.length > 0) {
          const fileName = fileNames[0]
            .toLowerCase()
            .replace(/\s/g, '-')
            // eslint-disable-next-line unicorn/prefer-spread
            .concat('-', userId ? (userId as string) : '', '.', extension)
          callback(null, fileName)
          return
        }
      }
      callback(null, file.originalname)
    },
  }),
  limits: { fileSize: 2 * 1024 * 1024 },
  fileFilter: (request, file, callback) => {
    if (
      file.mimetype === 'image/png' ||
      file.mimetype === 'image/jpg' ||
      file.mimetype === 'image/jpeg'
    ) {
      callback(null, true)
    } else {
      return callback(new Error('Invalid mime type'))
    }
  },
})
const router = createRouter<NextApiRequest, NextApiResponse>()
router
  .use(upload.single('image-upload'))
  .post(
    async (request: NextApiRequestExtended, response: NextApiResponse<any>) => {
      try {
        if (!request.file) {
          response.statusCode = 400
          return response.status(404).end('File was not provided')
        }
        const file = request.file
        response.status(200).send({
          filename: file.filename,
          size: file.size,
          path: file.path,
        })
      } catch (error: any) {
        return response.status(404).end((error as MulterError).message)
      }
    },
  )

const apiRoute = router.handler({
  onError(error, request: NextApiRequest, response: NextApiResponse) {
    return response.status(501).json(error)
  },
  onNoMatch(request, response) {
    return response
      .status(405)
      .json({ error: `Method '${request.method}' Not Allowed` })
  },
})

export default withAuth(apiRoute, HASURA_ROLE.USER)

Actually, this library is not needed anymore :-)

/* eslint-disable unicorn/no-null */
import * as mime from 'mime-types'
import multer from 'multer'
import { NextApiRequest, NextApiResponse } from 'next'

import withAuth, { HASURA_ROLE } from '@/middlewares/withAuth'

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
}

interface NextApiRequestExtended extends NextApiRequest {
  file: any
  files: any
}

const upload = multer({
  storage: multer.diskStorage({
    destination: (request, file, callback) => {
      const destinationPath =
        request.query.isProjectPic === 'true'
          ? `./public/uploads/project`
          : `./public/uploads/profile`
      callback(null, destinationPath)
    },
    filename: (request, file, callback) => {
      const extension = mime.extension(file.mimetype)
      if (typeof extension === `string`) {
        const fileNames = file.originalname.split('.', 1)
        const { userId } = request.query
        if (fileNames.length > 0) {
          const fileName = fileNames[0]
            .toLowerCase()
            .replace(/\s/g, '-')
            // eslint-disable-next-line unicorn/prefer-spread
            .concat('-', userId ? (userId as string) : '', '.', extension)
          callback(null, fileName)
        }
      }
      callback(null, file.originalname)
    },
  }),
  limits: { fileSize: 2 * 1024 * 1024 },
  fileFilter: (request, file, callback) => {
    if (
      file.mimetype === 'image/png' ||
      file.mimetype === 'image/jpg' ||
      file.mimetype === 'image/jpeg'
    ) {
      callback(null, true)
    } else {
      return callback(new Error('Invalid mime type'))
    }
  },
})

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(
  request: NextApiRequestExtended,
  response: NextApiResponse,
  function_: any,
) {
  return new Promise((resolve, reject) => {
    function_(request, response, (result: any) => {
      if (result instanceof Error) {
        return reject(result)
      }

      return resolve(result)
    })
  })
}
const handler = async (
  request: NextApiRequestExtended,
  response: NextApiResponse,
) => {
  try {
    await runMiddleware(request, response, upload.single('image-upload'))
    if (!request.file) {
      response.statusCode = 400
      return response.status(404).end('File was not provided')
    }
    const file = request.file
    response.status(200).send({
      filename: file.filename,
      size: file.size,
      path: file.path,
    })
  } catch (error: any) {
    return response.status(404).end(error.message)
  }
}

export default withAuth(handler, HASURA_ROLE.USER)