instantcommerce / next-api-decorators

Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.
https://next-api-decorators.vercel.app
MIT License
412 stars 29 forks source link

Body validation always throwing an error #487

Closed leahtopchik closed 1 year ago

leahtopchik commented 1 year ago

I've read some other issues relating to this but nothing seems to work, I am getting a validation error on every request. The following is my code, package.json and tsconfig:

// index.ts
import { IsEmail } from 'class-validator';
import { Body, Catch, createHandler, Post, ValidationPipe } from 'next-api-decorators';
import { apiErrorHandler } from '../../../../../middleware/apiErrorHandler';

class NewsletterSubscribeRequestDto {
  @IsEmail()
  email: string;
}

@Catch(apiErrorHandler)
class NewsletterSubscribeHandler {
  @Post()
  async postNewsletterSubscribe(@Body(ValidationPipe) body: NewsletterSubscribeRequestDto) {
    // console.log(body.email);
    await newsletterSubscribe(body.email);
  }
}

export default createHandler(NewsletterSubscribeHandler);
// tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "experimentalDecorators": true
    // "strictPropertyInitialization": false
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "api-services/storyblok.service.js",
    "pages/apiHandler.ts",
    "constants/serviceAccount.enc.js"
  ],
  "exclude": ["node_modules"]
}
// package.json
"dependencies": {
    "@chakra-ui/icons": "^1.1.7",
    "@chakra-ui/react": "^1.8.8",
    "@emotion/react": "^11",
    "@emotion/styled": "^11",
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-brands-svg-icons": "^6.1.1",
    "@fortawesome/pro-regular-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "@joi/date": "^2.1.0",
    "@mailchimp/mailchimp_marketing": "^3.0.75",
    "@mailchimp/mailchimp_transactional": "^1.0.47",
    "@reduxjs/toolkit": "^1.8.1",
    "@sentry/nextjs": "^6.19.6",
    "@storyblok/react": "^1.1.5",
    "@stripe/stripe-js": "^1.32.0",
    "@types/google-map-react": "^2.1.7",
    "@types/google.maps": "^3.49.2",
    "@types/react-datepicker": "^4.4.2",
    "@types/react-slick": "^0.23.8",
    "axios": "^0.27.2",
    "class-transformer": "^0.5.1",
    "class-validator": "^0.13.2",
    "crypto-js": "^4.1.1",
    "cypress-iframe": "^1.0.1",
    "firebase": "^9.8.3",
    "firebase-admin": "^10.2.0",
    "framer-motion": "^6",
    "geolib": "^3.3.3",
    "google-auth-library": "^8.6.0",
    "google-map-react": "^2.2.0",
    "joi": "^17.6.0",
    "js-convert-case": "^4.2.0",
    "moment": "^2.29.4",
    "next": "12.1.0",
    "next-api-decorators": "^2.0.0",
    "next-intl": "^2.6.0",
    "next-pwa": "^5.5.2",
    "next-redux-wrapper": "^7.0.5",
    "next-seo": "^5.5.0",
    "nextjs-cors": "^2.1.1",
    "raw-body": "^2.5.1",
    "react": "17.0.2",
    "react-cookie": "^4.1.1",
    "react-datepicker": "^4.8.0",
    "react-device-detect": "^2.2.2",
    "react-dom": "17.0.2",
    "react-firebaseui": "^6.0.0",
    "react-google-autocomplete": "^2.7.0",
    "react-instagram-embed": "^3.0.0",
    "react-player": "^2.10.1",
    "react-redux": "^7.2.8",
    "react-slick": "^0.29.0",
    "slick-carousel": "^1.8.1",
    "storyblok-rich-text-react-renderer": "^2.5.3",
    "stripe": "^9.11.0",
    "vimeo": "^2.1.1"
  },
  "devDependencies": {
    "@cypress/react": "^5.12.4",
    "@cypress/webpack-dev-server": "^1.8.4",
    "@faker-js/faker": "^6.3.1",
    "@types/node": "17.0.21",
    "@types/react": "18.0.1",
    "@types/uuid": "^8.3.4",
    "cross-env": "^7.0.3",
    "cypress": "9.6.1",
    "eslint": "8.11.0",
    "eslint-config-next": "12.1.0",
    "eslint-config-prettier": "^8.5.0",
    "html-webpack-plugin": "5",
    "http-server": "^14.1.0",
    "husky": "^8.0.1",
    "local-ssl-proxy": "^1.3.0",
    "nock": "^13.2.4",
    "prettier": "2.6.2",
    "rewire": "^6.0.0",
    "sinon": "^13.0.2",
    "start-server-and-test": "^1.14.0",
    "typescript": "4.6.2",
    "webpack-dev-server": "3"
  }

Any help would be greatly appreciated - I'm hoping it's something stupid on my end.

ggurkal commented 1 year ago

Hi @leahtopchik

Seems like you're using swc, which means the emitDecoratorMetadata flag needs to be added to the tsconfig file, as explained here: https://next-api-decorators.vercel.app/docs/#using-with-swc

Let me know if this solves the problem you're having. If not, having a minimal reproducible example will be very much beneficial for us.

leahtopchik commented 1 year ago

Hi, Thanks for the quick response. This didn't solve the problem, unfortunately. I will try and pull out a minimal example that's runnable!

leahtopchik commented 1 year ago

Here's the stack trace I'm seeing in my logs - I'm working on an example but I'm getting errors on the simple project I tried to spin up. Let me know if you have any ideas:

BadRequestException: email must be an email
    at Object.validateObject (/home/***/webapp/node_modules/next-api-decorators/dist/internals/validateObject.js:29:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /home/***/webapp/node_modules/next-api-decorators/dist/internals/handler.js:59:25
    at async Promise.all (index 0)
    at async NewsletterSubscribeHandler.runMainLayer (/home/***/webapp/node_modules/next-api-decorators/dist/internals/handler.js:41:24)
    at async descriptor.value (/home/***/webapp/node_modules/next-api-decorators/dist/internals/handler.js:105:13)
    at async Object.apiResolver (/home/***/webapp/node_modules/next/dist/server/api-utils/node.js:182:9)
    at async DevServer.runApi (/home/***/webapp/node_modules/next/dist/server/next-server.js:386:9)
    at async Object.fn (/home/***/webapp/node_modules/next/dist/server/base-server.js:488:37)
    at async Router.execute (/home/***/webapp/node_modules/next/dist/server/router.js:228:32) {
  statusCode: 400,
  errors: [ 'email must be an email' ]
}
ggurkal commented 1 year ago

What does the request body look like?

leahtopchik commented 1 year ago

Request Body

{
    "email": "leah@gmail.com"
}

I'm having the same issue if i change the DTO to

class NewsletterSubscribeRequestDto {
  @IsString()
  email: string;
}

And just enter a string, it then has the error message 'email bust be a string'

ggurkal commented 1 year ago

Hi @leahtopchik

I could reproduce the error you were having with next@12.1.0 (the version you're on) and can confirm that upgrading it to next@12.1.1 solved it for me. I don't know the underlying issue yet, but have to dig deeper for that.

leahtopchik commented 1 year ago

Thanks that fixed that issue, now I have to figure out what broke from the upgrade. Cheers :)