{"_id":"@storyofams/next-api-decorators","_rev":"116-4b3fc75a6e03ca6256586d3f8d4c623f","name":"@storyofams/next-api-decorators","dist-tags":{"latest":"1.8.2","beta":"1.8.0-beta.3"},"versions":{"1.0.0":{"name":"@storyofams/next-api-decorators","version":"1.0.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"092db897499e54f0eeb623f6c755e56e5bff03d1","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.0.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-YLlfjSFvMX7AQmtpQgMfv8GrShI+i6Pw22PVIMUpQphanSor03P1JG337Um6uIijB7GRa4iuIeHyguIVGYxDgg==","shasum":"ceb8942e6f1dba18ca1e6d1d9273af4f845054fc","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.0.0.tgz","fileCount":42,"unpackedSize":85704,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJQWSCRA9TVsSAnZWagAAyIIP/2ckwhVSStnSQzji2Fa4\nxIBwCHLtYJE1H8Pr7lcQ97JjDRjINwq/hAm9BawKC9KX8WQzcvysFDJO5b27\nF4dyoDlxWtZIs0vO9P8jxE6UsVcEfAEg4mfd3mZDkYOkgHDMWgs8FBU5yYNM\nwiFeYHjh/qiq9OHBEeIKBXxhqbqInWrFLtced3bZDa8xj8k/jO+IVWIagtnr\nr3Ncl1tLiqFvFmI6pX9duArIDrb/DEXDSlTVYInDkaL0zdy/5EuhmVAe/jyt\nQmfJRnv2gzu6rlmKgjuAUsxRSTwEDytipHNxaB5mS9hNSF4meGN06PNoRY4z\nLIZBWw0KIrEgiMOkNmZ8ff/FUeWR4JC8i0ZOEqfsd90QjKlqoedfNMAq5M8u\nTHt444bmwQsIYopsMhhEyfWkRHLStA6SO8xzGO0nVpi6Sii+397A8Xt3phOS\ng3RuDJH3YhBPFMz27+/TjpInZ3ryCMCYt/bJqDqCEkJeXwin9coFS3QV1u+B\njgohTZjYDvx+dyyPQ5iLP4sdmmX1GKlj5YoWRIT6zbTGv/lCk66D+SqcgHUY\n/zO8EhI6ScfAjDwCCHO43OpaPsOcAZdYku23PifE7oReQZvA64rsBOYdA31t\naA2BmhxYRIWqNVvBV+B3PubI8ShnC9mDyHAyfN5f+UADTwxbt5EiZ1LHsV5F\nCFk3\r\n=k8Tz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCtbf0IsOaCYm35593YRXwlZADlHwtW8N0a13Tp54y7wIgIXHlw2oj7sXb6q2UfzzPO1tEBlZc4ZW1kGevG8hwT5A="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.0.0_1613038994155_0.5075611901588748"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.0":{"name":"@storyofams/next-api-decorators","version":"1.1.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"dbf2d9b89f833366f27e713b0e63abf8cabae1f9","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.0","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-M/cSgLElooFUess81TtkZmuWio3STGzoDz6pEIhVyrFy8vffnd471HGzvVDLNnQxdh/3R666qYEFmUGL9HJflw==","shasum":"606551f124a36b53bf147249f6210204742b4fb2","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.0.tgz","fileCount":42,"unpackedSize":86899,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJQy+CRA9TVsSAnZWagAACwkQAJONYFQAogbpYDl5PwWz\n04BvaPeSvJP7kFbgXYsYgtkprhaCPsycFeGNwOvkFw3+k40wwQHtiNug+i8u\nfxSXEjtMV9tT+qZs1CgGhTc38gKhx/i7/WEHc6x6xmM4F89MrfbxzFiqHQjq\n77MKQSfyMTJJROwgbqQQnPIqg6akwT97PAy4Da4M5OhjWqo+R+D+231BYUBh\ndOSWtjRCwntVAeEFnTWqOEMMzk3WLbUWJJOe9eDyiDQcA3QtTocB6qt4mo/T\n7s6V8r58JyiBG/P2roBwNG9pLHyoJHPSJ8EjvnbPmxcPEYFbUS4BVqvsv3TG\nwSFSe32vOCHgLyx7W1Gce6we71OcQzsd9e3purZRvCYS9z/4nL8CjFpvmt1m\n+/KZ4eELav28s9JrbQcqJ7qWHrF3rLX5dx5OIRao41YcfW0NsQBP9dygUk06\nY3E/lpsIzNyXd45oBQuZtxsqw65BxK17zfN30qW//Rq2S9M+rFGpTXaBXNEi\nXPXPGjIQZsDzktjUwkZC9Js5v9gI91BR78MKao7Xvn/1neD9m+lk96YRkkVl\nce2ldjI/b3WKT+RowE31YPPQCQ5bcwy3yPMqxzBoc06U+R+3dMpTKd1KekiH\nu/z5zWzTJPts4ER95MAGopGSyQ5KJQlHtbRaVbiUXfNcL8iRMPb4qJMAJIO9\now1N\r\n=IM3x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG9fo2KbsFeTlCHGAxujnWev9Uj5nKdVLn2PadDuNxz1AiEAtFH3xiEvB9YX1VbC2WxgMX91YZHTixZ+kpbwmY+lf1o="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.0_1613040829594_0.9445131361703358"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.1":{"name":"@storyofams/next-api-decorators","version":"1.1.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"c3d7f3f6255f6ce2c3672563df4692d75d2ef79d","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.1","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-9NVIdQGwVjoVexhXOyygxkZ7J3FEdcutgtDpe2sGtD2G6RXh5fJmxYgMD2wornXCUGMGSNpDB6ctNZXtjVvlyQ==","shasum":"0a6c5856c4a14922c7e73bd5dc332290d68151d1","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.1.tgz","fileCount":44,"unpackedSize":88690,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJQ/+CRA9TVsSAnZWagAA8DwP/RNiui0n3aecAgkv2zI+\nZKXbBmX2+hvxYaqBxrvzV1cpfx1KtHGwemOIi8ZZrSCJSIwuUJQDnAl5M0bm\nw/hHLtNl11Uzxi9zlQOUgMFDPEnrxKDpqekAOKIGylmW9heWxhSriVouwzE2\nYSnopkyezshWiD/CY8B2LoEZH1Z/+8r4Jp0sNa+5cAGBrth+c+KskpbsoAz4\nwFn3kwhlcNhF4x4IlIwE42VdYPHl5z+bN+DTOrFYZCmO6EvjOfI8hXWRgakS\n6UUQlnXYDzlbelBu0ggsiu5L5QnDeNBiTAvco8t/kFt/S8rU+T/w2ZndO7tO\nrP7pXfbL0CgbvQQZ9Qby+6qMYzx8Iqspv3mMe3IkbtOV6P/Z81l9VU2/079P\nKM9uXX3Kv3CJf3BAr2D+kuovdTtB2/cqdQcr3rdg13OsEQVSOm++4ypMEJ3F\n69NdHeT0ld9hhxzBx89cLr7njeFecP5/y2CF0L6oONtSbnbaQXX/K3H/tMSc\nYCXujC61POD/gL82h5qDLX+4EZw7lM4YPA/0X2h5jactIy5z6L1IDxRJgzVX\n+cvr45VzIJuNYdGuMmrJBa8ZMcChRbFti60XL/BK05fMt3QdXh+BCPLj9oOP\nQD5WsKNIjpYqcP3AgqN3M30bA6kfHMFXB1IiX8tjnp3oH2MEDH0jUsCdJ/Do\neYOT\r\n=uz+f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDXR6yuc+RqpkXzXm9O8XKiszSqpBuTq4H8GdBi7pdiywIgWWrU4JzQHQkiv8QYWOumYPn+CZoaeXQldXtzDJpSKaY="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.1_1613041662156_0.528240860745095"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.2":{"name":"@storyofams/next-api-decorators","version":"1.1.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"d079ca52bdf1ce8ebc33f77f9d540521de3b724a","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.2","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-vQCzx7Iec4jsCD9+doSDFgqEYfXsaMgwo1L5GW+/5+TbKaVt3x3uJfP7UfPxCRTlE94xPYU65VJ+kuEAh8kjdw==","shasum":"9f68e1fc384bab2ff694e502ce43635d76c336af","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.2.tgz","fileCount":44,"unpackedSize":92584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJRdCCRA9TVsSAnZWagAAbsMP/2vthSTsbrqfQYnK1zka\nzEAU6ahogI+RfrwWAasgJ1NSWNyIiXkiaOvzHiH2Zdm/AQv83hmrHBhkDx1q\nAGABPT/kD8i5oBfqHSPad/ZguQcv/4iMbRUcahttAYnbAN79/n0tWTyj8bgr\n1uye1LmBnXkzBmDZXhojRawrmRe9f+vB/nhr2JGsYmx0pF4ceifgDzFviUgE\nsaBKupWoOz2xpqBKT+k980WryRoEBYlhPtBDhDth8CwlQTL331ajISIGVluw\n6pnTmf6DiTvplxPNs/pmH4zy0OnvlaE14boL5dvZmn53CXr4svmAp4tKmAGg\nM26bOKEDzOelw33apHOVAbzfvLUhhy5valvtpk3MW/F+7mhMAI/wav3RL+uA\nEzojxVSLRqzXd+DEZbm7qvdL2XNmQ1XvTrP55BXiBid+EFar2lSQcA6ybjyH\nvrw19gQbgIupB1PC+lX34NwXYB+CUjRHfhSiG4PKFatAbnA0+avALcuGCVkI\n4MvzpF3RYf8YYe5a4tl92NUFwWWyoFO9hSQgAyYi3WWGneE1n2s87FVClzf2\n6JwdHE1jXeXsPrIQ3CNeh6EylAJ7KlALC3CX/618Y2hjUQeycXbT2uwKqCbe\nVLCE0nD6+0F2Hy2xAxFMm6ybWpcSFI2dGSYoBRpF2wcewac+pbQUyDURqKGV\nAZ9g\r\n=ztoi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPwvXFMon8dw6BSXvEtyRzQTNeztgHQyKoROkord0PXAiBykT4qBrw4TAn7sOIm7uXH54/rnAsnaJoshN6+AWfn4w=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.2_1613043521542_0.540926903967158"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.3-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.1.3-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create structured API routes with Next.js.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport {\n  createHandler,\n  Get,\n  Post,\n  HttpCode,\n  Query,\n  Body,\n  NotFoundException\n} from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await User.findById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: any) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|             | Description                                                                |\n| ----------- | -------------------------------------------------------------------------- |\n| `SetHeader` | Sets a header value into the response for all routes defined in the class. |\n\n### Method decorators\n\n|             | Description                                               |\n| ----------- | --------------------------------------------------------- |\n| `Get`       | Marks the method as `GET` handler.                        |\n| `Post`      | Marks the method as `POST` handler.                       |\n| `Put`       | Marks the method as `PUT` handler.                        |\n| `Delete`    | Marks the method as `DELETE` handler.                     |\n| `SetHeader` | Sets a header name/value into the response for the route. |\n| `HttpCode`  | Sets the http code the route response.                    |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```","readmeFilename":"README.md","gitHead":"414a7eb89dc45378aa28d0b82c6c1ee315d8de07","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.1.3-beta.1","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-fPgYTXU4muVCedyNmQUn9Rsf6Ma4jU822C4if+UJdRdi7aMca+zo2hpfbqr3Eoqw+GkpCJbmIK2oWSPQjResYg==","shasum":"b8cd61f36d364f9e96b9812608372cbd5ab0275d","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.3-beta.1.tgz","fileCount":44,"unpackedSize":92868,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJWGkCRA9TVsSAnZWagAAibkQAIxtaXSBuax5vYlqZ7AM\nswAsUlZ3/xMElukDx8OYzzI3og358pCQoP+5P+VVPslg+1LeTY9HxRLwCb34\n5cLmfgWeULAtkykjT9OAat609jLljI6f/0VlFLKjf+UpUcD6O74z0InTljS/\ng3esbl32KGFncvR6LXbz1gVDEscPy5Up9fBi9QxNT4VzCa1xWHnh3klAtMSL\no9JhF6mXbBe+ZszaT1FynPxdYxU5xf7DzBWc8JhYjgSuwW3e5RnzGa6g9PZl\nozg712IljIz/61zvHxzNHSnkRe68xzCG+LNgxdsxNrwj/9L1jRyuc4bN3iHV\nPggx/8SsCpJbzuJzXYzltfwtb4bQom+4l3kkW2Qrp3PI2IlcXJuY3xB86Q9O\nsrzeEJWqBFmaIQFAbN4oXCTowKbsTdqe/AZSpmvJCDgtRzKb1Pj2lRVvKhE2\n0RnVQOieotmvIk0HAhTkV83ciX9cjdbnlHOJ1yxFnS0b8sLoZeA15DN6B6/B\nrrfDOYiTqPZsS2FISzKMe1BX+Z/maoG9Fhi6xyNs0H3zTiR2a2fJU05Y1/f4\nEzzX6uK8Vbn3GhH87OYqp2DiExM/2P6U+5eisnpZ8h7/xw7J/fc1wZCsIpdr\n3tulsZkVc+Zrg8UfU0XRz2vkl1tfpwRlFofriuPJ0GvG9Kmt1GLteFg9ogfL\nFaDN\r\n=BKU0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgDASlzoZhCXO/4OvUIr3aQ+xJqIPgz9UjT9Iq+MmzXgIhAO1gNFjjIK2Rbg/jYKuVcY35j6N1GeIHxZcSjM9RpZWn"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.3-beta.1_1613062564194_0.4563961024368386"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.3":{"name":"@storyofams/next-api-decorators","version":"1.1.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"26da90012f731e9516c5b9254b8a0ffeb5f1082d","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.3","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-WnmWDMRYccNwHV5bNjpz2YvllZMK0Fj4ybbSeP5GtREwaBCCui1Vp7nGAaeX7KgbE0whv8SEvxY2RBYYt+SBxg==","shasum":"1fdff53ea21cc04fb8f18bf0d4d92e376937ee46","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.3.tgz","fileCount":44,"unpackedSize":92724,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJWdWCRA9TVsSAnZWagAA57gP/AgSwBn5CaOpODzKGdsw\n4jD8Zz4XvKFPRZDvT67gHsyplEh161YfTgyvL3njZB5hY+hYtD5wVCsf6SKl\nssj+q8b/KSBsz5SmqB+BdM1lGsSxAYmYBIwSiqKvLis+pAr6Tfonx4LZLmKY\nuK/TNWAdQZJ6Supgi93fM1JFgrGyvh7MHYgLnYRwUD22HBXVN9H28NIcjEdq\nTuAyLNG45rs7ScGFncueJnKwVQyvN1K1YEKSiyERuIg01Z8jz0+kDB35+Xwa\nEzp1UqJSR21wQWjLV0mhqeqDPqlWpqLB8oBXyYnfGbUsW139wd5XhQbQSCjk\neSDAJ7t517TBwQYNtvS4hOLH4BoOqoKja/aUVUOA7Smqm4lyhtCGrM/3REBe\n+tmmiHZ7VVDf1XdEr9TjWm7LOiVlrpkXR3mHURh3gKG9knrCoasx3oOHr2x6\n+OuLaknDnAE4evarj6Md76U+NGBMnYQVXp5W6UJ3QdYiNk4opvE/JT4gS/V5\nK8T+qtu6pmyUJuNuAk5sKp2EUpDCjs8NweBnlN1Jz54VcN8pY5O1OTtaE9BL\n9DDy48XNxyyp92MdS+Mzha5R4M8x0pa9Pvt2b6ibhfg/GAyMViZNk1TjszYh\nBofpQLDoiKUA2D7wXpVpODabkUz6G+nXy5nwV5NffgAbEK2rAFRNWut3s54a\n3gS3\r\n=/PHa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBytqI0AVAGAV8uVLI/pth80PPYEIZ/sewPehj03DNeNAiBy24bMooU5RCnxRfQURhINwzIltap0vWRYi2y0cFMkEA=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.3_1613064022307_0.8668059246607378"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.4-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.1.4-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create structured API routes with Next.js.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport {\n  createHandler,\n  Get,\n  Post,\n  HttpCode,\n  Query,\n  Body,\n  NotFoundException\n} from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await User.findById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: any) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|             | Description                                                                |\n| ----------- | -------------------------------------------------------------------------- |\n| `SetHeader` | Sets a header value into the response for all routes defined in the class. |\n\n### Method decorators\n\n|             | Description                                               |\n| ----------- | --------------------------------------------------------- |\n| `Get`       | Marks the method as `GET` handler.                        |\n| `Post`      | Marks the method as `POST` handler.                       |\n| `Put`       | Marks the method as `PUT` handler.                        |\n| `Delete`    | Marks the method as `DELETE` handler.                     |\n| `SetHeader` | Sets a header name/value into the response for the route. |\n| `HttpCode`  | Sets the http code the route response.                    |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```","readmeFilename":"README.md","gitHead":"6dc537c271080497918bb58d44d01f71fe502b0c","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.1.4-beta.1","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-C4KKAZz3+PY5x201slfjxUA2XFxMhfCPxsACIzOoDZ0Wqvn8YiOMk6ifljCWET53EQ2oUhYo1JzhHcJRtPBzSg==","shasum":"885e877032cd2cb18e8c8b5ec8d01a32140e1180","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.4-beta.1.tgz","fileCount":44,"unpackedSize":92818,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJWhlCRA9TVsSAnZWagAA4psP/2vw/IhzBAghSUtqva73\nDlCZaXF4hrFpzhWmNUfNDzPkCpvZUXEwMXQvR0d5cW4xd+4VC57rwRMwH4dl\nXgLExsLVEtukW/GICzxMm2jKlJsrD16Sly/soTgDFR2rRpyGUm/NZd1UeAdP\nSvDWjToVtPRjhZmsWsYWIWRs92KvBJCLcCYZzx05dyGeFa3kPId/jiIgHsJB\nyOCmmeH9wLH7L1TOmPcO9giS5a6pjfGmvBqRRSW3P08WateMW1LfI3F4OuJV\na5DV125elQtweqXSB0v9mwRl9R77soqH5nlvGdwPvxEyN1zZNIQCWuT/GjuR\nodwV64vy0dSjalLieAC3t51Nv8YcR2mhZG/bQsJ/QJdjngBagoOZgXaa9MsE\n1DH1tMEiZOt77kzx7ATlS8oysy1q/LmLDsinAkXgSY0XD8ZocinWodnJjcYa\ncI3d5puPDPiaynn8OHpHWa3Qg1CA4weUE2yRKNzEDXJADH27BLIrmJcul6+c\nvjy1jcJibW7KNXm8WP99AqLp4vwVFVJiMRNDnjqkh7AyA00wc6h5vcGHmzAG\n7o1IqYX3+GymSZn8MckrqSz62mr2s80z2Ce+4/PkO3fnVjjZ6jw3kFBSCZwG\n3uv6+MCMc02JAcqW3dBwhhDkwiQzmWktEsWdTq4FIwVpmErog4hf8LxbKCln\nY643\r\n=cyT4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGO14UPw1303wfZWnkpAwe9TDaGiiS2mijLh9Mp8S+wEAiEA8U/pof7wAI07XaWUB3T5GYAGi7P7hB8tdeG7BFUSYyg="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.4-beta.1_1613064292949_0.5313686631973553"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.4":{"name":"@storyofams/next-api-decorators","version":"1.1.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"407009888549f4aadc014a450dc289a91501fa06","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.4","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-QMc7FgJt9eeILnU8svmGN41SRdp7kp6yE1fkpU0UXpkLh74l/9XvWh67M4pW4leSVkN622hlqg5KNtcehLe/mg==","shasum":"da39949401d9bb60c5792a7faa9a66f48b95a895","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.4.tgz","fileCount":44,"unpackedSize":92576,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJk2WCRA9TVsSAnZWagAACrEP/1h6SqqiXml02gr5V5M9\n1KTG+8tf6reWkI5t0oeROpuh5USGvOW8mNfWEj8zWLShxLKcW9uY8yLFaqbQ\nMofLW93HLGAwrTB/KOL/CKOkXwwNQDjuGo8yGtZDxDosiwI0GKZcdDZqgtvb\nA69A/Ky3rHk8Ouv+08hXRMN11Ur4zICnonCZSeLhVkkJ697etvfxg8JoiLUk\nAOONzbvW30ZoZEKRHThbpmyBAiS4wvsyjxR44qkzxHy3o8ZZZUeD1mnZLj/9\neetAsoxZ4iTWp2dW+93GyFMZb0mVEA2VEKmknnaWDpTJ78y+5R9pjeRIXZkH\n8Go4l20+fAYXYKdxMi6CMsITImf2lyUtxluGtKwz/As7qzinXB3AJt3c0Fh7\n5G2V2nbnl2/XzgEMaF9gzwbPU5HQQEnYSAiuY6QLQFrELgZT5nfBOuOigAeB\ng1AxMO5eorKuNMPtszkjOs0+ylTEPEgDFZ9L1igkjokg8gF0uHcg3yF7E/53\nC2jhMHva7ddBl0imBIow0he0uXrz9jextkv1eVvXlbV0CTFA7npxPsUO827P\nGL2JD2DeNBAbpXvPDfM8svtdvN8bJxRZDXLY9+SLinvHb8DZ6TSFnTXroYMk\nLzzDQb5OgtTCIEVPD/2bPxsUPPYPvNEOSBI1v1hbFJ4PUYNEY0+eIcd8iENV\n2efG\r\n=1dDe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCGE+sneK6jPZqG/qErohgCjntFA3nx/oC6N5WmI7rfRgIgLTo7JXoG5h7zk2HyGpkzuosiH0b6uU4QkCwiTE6J/eg="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.4_1613122966328_0.441294593510682"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.5":{"name":"@storyofams/next-api-decorators","version":"1.1.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"99834eb9d97dada7425a01ec6291d3b4430495fd","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.5","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-ni20ut725W9WW7rybvqBszRBofmAhiplzqeeyiJiOtXV7539m/3GSJbkAHeawd3gy4KdoILv0diSZ02baId4Qg==","shasum":"2a6d21a98475487ee99f5efa0488e6bc325ba313","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.5.tgz","fileCount":44,"unpackedSize":92773,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJt5WCRA9TVsSAnZWagAA6iYP/iTvU7iS0ptAOsFARs2T\nJL5hVYHqSbwNOowi4Az3izASx4DlOaWAf7HC5nCxbbW/C+yAZk+kITbMwr4x\nCqAkJRkH1M3Zc5qDhIsu5OHXWelHI2ChdI79g6hobABxWBMfemdqeMkTskL7\nlWl4+lOyShjfBpuUzmuihGhAfNBeg9HM18fEbHuvXsvtapj84qM+vNVr0wXd\nS6SloWphGnjMP+5KlqyYxSLN6gZ7Hjkkvvr5KYDo7lLpzCNVXKNOCG9KEQK7\nFSuNLFr6DbnOsbZgLAT2nHr9O7cK0AuxBVc/+cuMv5byi9V+pIUreIFZ6WoJ\nLS5Lf25ndZRLjMAjGdLSyoAnVmLeqWP0RqSJYPSh4pvqaAw/+WzVrvn6xdeM\nYy/CUGK+j8zZHo6QgmkscelxCxpROvTE4URh9fTbwL8tBvSLJkTb/NoYwgCN\nnwfJL4fs8S/FhUk9YlfeEbVLVg+LP1wGe1HMhokmw1iXbaqjZnr4S/0H5V0y\nxvyI+pT67JHjtaidE1fmuIH2i+HD/t9WOmVMD+Hz9lJKcvRDi87eFa3Gd2cZ\nfBgr8/4QDIQGoJ83IPtTMY9aXDGJQ3DMDDgwXw7V9dYr7vOVcTlm/h5iwTI6\n0/6vIQ8ST8eM+QhsQTMfkehi7vXZnD7cTIZyC9xJxKHtWu0+M2Ilw6UYtIUn\n9bJu\r\n=NR86\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgQsmzjJNP5U1NopL+E8mLQXiCNO7Lbs42OGXjCCinMQIga+7LW9luF/lxguMLshbuid5z410nTBHJJjDX04HlEBE="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.5_1613160021556_0.32691717259187913"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.2.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create structured API routes with Next.js.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport {\n  createHandler,\n  Get,\n  Post,\n  HttpCode,\n  Query,\n  Body,\n  NotFoundException\n} from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await User.findById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: any) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|             | Description                                                                |\n| ----------- | -------------------------------------------------------------------------- |\n| `SetHeader` | Sets a header value into the response for all routes defined in the class. |\n\n### Method decorators\n\n|             | Description                                               |\n| ----------- | --------------------------------------------------------- |\n| `Get`       | Marks the method as `GET` handler.                        |\n| `Post`      | Marks the method as `POST` handler.                       |\n| `Put`       | Marks the method as `PUT` handler.                        |\n| `Delete`    | Marks the method as `DELETE` handler.                     |\n| `SetHeader` | Sets a header name/value into the response for the route. |\n| `HttpCode`  | Sets the http code the route response.                    |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```","readmeFilename":"README.md","gitHead":"e2965ff0eaa72839d8b56f1cc58cedada4ee4513","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0-beta.1","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-9sHJcgppqV2XLwIuD2399vl8d59mtr+sN8qq6vifhWyPPvCDCbDzFjPoRyTAT1QzLuoDGKioY/u+l+V+DRAzdA==","shasum":"a19aafcd7f228103a296c6e3a24302f7460ec1ce","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0-beta.1.tgz","fileCount":46,"unpackedSize":97553,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJu6yCRA9TVsSAnZWagAAYtUP/jGztggSzh8FxsC1bSiu\n4sR2EpWyzzgHjWe7tVkf91ERPFaEHOKwfXKKNCHN5bq9OJ0BjwpYyfG05TcP\nu27RYCFF4OuDvDhjDaf7tqOnZBke0oMfaLI2jMaR16hYoTdVD/tydCBiNlCI\nhlcTCT+WLez/4nNbeYNjydEpNxAfr2cHPpK6lDrBStEf9EaATnrhSxiRoqVL\n3i5dOO2pwV5N1WEzH0Iqzbygk1C7j5BbBbbor8eDYkgZA42RYEUq0ZWZYE3h\nhfG78pBaf/L+crD0anbphI79PWApM9ZWmEUMUOEcLskv+HVOeQjconFHNncG\nekmCMZhCtk24v8ZX4ftj14oTA9TpOryULiD/P840SJ0rfXkw1KFWnMvm5Cx4\nxLLr0n67XZJ0Ysr34oO88z0ymTBGUn+ruQ+8CRjvhZriN96kkXjKxkKtCOdk\nW7+qViDpVheETBOXlvuISkua0AKcoSbt2DBf7WLMXke2fSAENZN+IPb3+AJR\nSAYFrgDYfL8PYuqX2hOWtb5gvt851VRSx/FvBU+WN/EWqviULzSQcAaWEaNL\nOWipMx1wyO9WZmGwf6swAQKchbEXX9MnU1e7ROLrnvyxb1dgKaz3tIToP9kF\nCNr7S/ychNNoXNqS6eF5Api76qJAd9iiSwtkQQc7c5uIfmpX6/qFCwSNEe/G\nX1gj\r\n=uZV/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICgh3J12OoADifZodb/0pzOqry4vy2Dum3VOqcNRSylXAiBZ4FlO1oLxqGUa7c89tpuceyB6czfmFQ1CrKlUPsMiNg=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0-beta.1_1613164209846_0.2956083584279281"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.6":{"name":"@storyofams/next-api-decorators","version":"1.1.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"7136acda6b268048efefbbeedc56cc105f203c2b","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","_id":"@storyofams/next-api-decorators@1.1.6","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-4UuRONZhW4FN/wrbVD9MXDS2mtUAwdPdimOQpHAi/B7M2oQGYLQlS8XWmAcmDldAkFxpTyBXOlFXfxO9uWyJUA==","shasum":"1a47eea24dc1eaeb33f26321051f633484d950cf","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.6.tgz","fileCount":44,"unpackedSize":93019,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJu9CCRA9TVsSAnZWagAARNcP/jB6txv/IslioUh8maHt\nzPzajazsebXr5zFCsHOL9Y5xkvggpLVuhUpjJ6dnbR2Ukrmi0uyVe6khLOOB\nudIDMReAuGlfl25hKRSc/UJBdsZIkG/XSOFm9gtT3KXD7a7OzGdfyjW0bWkJ\nedx+nu0YWwYSKxlDF9RV2nD/6k+lpx2dCb+qtuhE6qcPH4NKyOLwYV5+YD1+\nRRPWpXtXR9xbW3j7qbWWKA4rxaxIdS7PVh/RMTbp0ADRJfgAQhZmZ4KMboaC\nOwRas/WZFoq1Gkmrm4jED/8RR5nNre8aAXTcnfqq9YmUKKvqb2D1s6LQcNOX\nlW7srukadtMK54XWfFolisxp8Zp07/QpTIeJyWVJ+InSdd/1+P0QBvqr5NiF\n5pRISbayvWSuWkIV1n8yWyouasu+AExfYPWTJ29FEpflni2Aer3fP+PsUd0Y\nskeKWPoBpftj34NfdjDrmFhWuW1f/7YmGnwNlyDgrg3DrAr8NJJcrfJ3PNut\n4W/bVSAd/uYur4MVRuEX/f/GgN1DwmlAGKuhF4P6HH9DXQ9PoDuz5pCTK/ap\nTKGCt/D6gNFqe51nW28MmboYnYJqHjD7hK4g7M0I8Ad5+007O8gtRhWr6xNv\n2rQuHJyyYeyXCuqgiUWrOvnhTGUK8pVu22McDdKxicZGCLLZYyokhhumYzgQ\nlWLd\r\n=cEkQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHXZgHf3Hg3Kj+mIbwXvdlk0HZoVA81ETuKXjcZ5Qhd6AiBu/sjjPvtYWfCGqjigYYAHX/G+QiC766pl/IgYVsWPcQ=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.6_1613164353951_0.8111702271670107"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0-beta.2":{"name":"@storyofams/next-api-decorators","version":"1.2.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Post, Query, Body, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  public createUser(@Body() body: any) {\n    return DB.createUser(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                        |\n| ----------------------------------------- | -------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                 |\n| `@Post()`                                 | Marks the method as `POST` handler.                |\n| `@Put()`                                  | Marks the method as `PUT` handler.                 |\n| `@Delete()`                               | Marks the method as `DELETE` handler.              |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response.  |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.          |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"3f4e9b98a0bb821a4c90be6dc5468621a605c8fd","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0-beta.2","_nodeVersion":"12.20.1","_npmVersion":"6.14.11","dist":{"integrity":"sha512-+LMK94iRkJzMlYFfeRj2AK7pY+WhwKr48W1R4U4+zJHsp1fmaP/qlNtRGnIu8GDeAnR6JlA/anAcAoRa+EoaIQ==","shasum":"c2f45e60f2fe091296395e534ada1838467bc70b","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0-beta.2.tgz","fileCount":46,"unpackedSize":97883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJu9sCRA9TVsSAnZWagAA4gcP+QD0xRvL3nsiSM5Ft18r\nF4IclbsaiKjct0/JuB9lT+UMLgBv2V5CXfxcPWZLlxfkT7u+frDpqt6unA/6\nEgVLDRmDsiG30UcnvJAdo99m8jDo7rr3OkNXog5I2u5zYL4eUZXCYplZhU+y\nTCVL4EQhx9MVeUx25UO+C8yTxCIuUOJ+HSHcuRhSJzoaX5Oixfls+Y27zUsA\nK55FEOymwYzAezp6TeUAL0YgCzO78f5464rVPvXr10wh9hH8XbneKHcY5dWs\nqMjEOoXFNWwOqgI/3qPn/vjrI1jCemCaCucKOAT0ToZ/szAk6kV6oKZVDayH\n0mT1dlcN7Q6mW0REOmeFohE6+TJy8ZCowQaKkK5aflLpOu6O9MM+XiWFrMFD\n3wgYDVF+24PseGNUZT9U18dXkeRmIskdSBSC87CNz6zScGk651J3fFAVNQtc\nNK2fSijRhKxTOz1QknoQUfwhx7Oc/0g6TJSj0a0ebt7HMTlzjKIZy0KSX8a2\ntGcgmzVjVpR+rYm6+6RgfKYaRLaCaDXQNuxOEgMPLNt8v+MkAbVfpbt70j/2\nGHQRrgekqYqLZdSAAk85be6gKSIYEZnQ8am6qr2KMzxEfslPBxvZQOPP7i/7\n3TGG/hPZyIwSSVkon9BPOyQE16Iso/Sn7eaLan5REVfbcY+MXIWBU9cWASkx\ne4Rl\r\n=X7oX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcE3M+pkNwk8J5MFU4hTBhSLky2YsraxOKXExjSZVNRgIhAO7ie/+6LYm1x5ZC5mtAyCemHgzR6pC2PdZj5KvrVspP"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0-beta.2_1613164396292_0.2547200451054217"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0-beta.3":{"name":"@storyofams/next-api-decorators","version":"1.2.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Post, Query, Body, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  public createUser(@Body() body: any) {\n    return DB.createUser(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                        |\n| ----------------------------------------- | -------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                 |\n| `@Post()`                                 | Marks the method as `POST` handler.                |\n| `@Put()`                                  | Marks the method as `PUT` handler.                 |\n| `@Delete()`                               | Marks the method as `DELETE` handler.              |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response.  |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.          |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"ce3d0471978cf3b3297261d4624e380e28087590","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0-beta.3","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-7GhTeW43MZwzdQXQvay6v4mo6eQC3UXht9iosJsYD9kvog2RGTilmt1M2ZnIKoAuRpvfc/XP5YZEj53sNEc8uQ==","shasum":"df93f7e698b5014b386cb8a99e6a2c9e62b84038","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0-beta.3.tgz","fileCount":50,"unpackedSize":111986,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgOpQUCRA9TVsSAnZWagAAS5wP/20I8UDvxVkyJ8xJM5ir\nwQDW0jlGyv3BJVRCofLgY9HxpswFhRsaAdBjCrNofI4cMC9Ds1e45MB881Et\nKSjQz6SfhKz/fBlUJgGbgqaOA7DWSSP8L8NXQ4B0ygjFk+RAhptzsf9kK4AR\n0ZcE88nZ9TXI/dDvxLwGgkxKGEMZhYinYqQLIQe8AWqrJPqtqmYE4R68UT9t\nY6k+Q721m4BTgu7mOl4coxv0gh/MkLuce0XSAsr1dy8dOofubQLYbzokgHgX\nejFgmK3nrrCi+HFGe+cbKUtxpPf4sxcsPOstTtmwHJ/jRzaKkXA8NJb6oMLt\n3S+TfE5UxI9SEGWwiRtHOtfBlBAgwHwxxvzP8qWDyOwROsP25vKk0DO18il9\n0h9UoYl8itlWCWy0s3Nu11LAZQcPLarNwk4j6obI/qiXhWWKh00As4EhWdx2\n+ntojJjDyEtnHMzIcobP7WxjyVbFl4ZSCyhrbCqfgLs2+3V+rqReoxpIhSuJ\n/+ywGIjUd8gZZRa6pdgBSPBs55sS6NAUBRyTxhEhKk7L+2Lh4+IrQOnDiV1o\npafMAHQMj+1OvkH3urUSamfsE9rdedRQzSuz4I9WjFYfxs0o8QkyZrncqblk\nlaJELJ3vxSvKwXiBnS4Yl6bsaktQs6Lia5e3T2FP650ZCzBqZ5eXD4qKhT1A\nvU4H\r\n=gRKT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC5ziLhsx3Yl48xXoNnFkqXLPlNiH199qW7n/EG4hWEgAiBt++KRtdDE39HNjR5/058lycFoflN0/d7nLwkvxvtIXg=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0-beta.3_1614451731440_0.7948580432305123"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.1.7":{"name":"@storyofams/next-api-decorators","version":"1.1.7","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"31fcea6d18006dc7918346edc051e19ee68e0f10","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","_id":"@storyofams/next-api-decorators@1.1.7","_nodeVersion":"12.20.2","_npmVersion":"6.14.11","dist":{"integrity":"sha512-bogfV1sGP/IzMhb1RB3sdZySdFMPMAXa2yGVcDHrY5azz2nT67vqU5FnmGmO18La6uyUrJ27TVe3TH4cUOxaHQ==","shasum":"90de3ed9ac6b447f6e75d95d3f8a659d403af628","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.1.7.tgz","fileCount":44,"unpackedSize":93982,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgP5jMCRA9TVsSAnZWagAAeqgP/AzJGoQ6Xh6ISwaKIxbc\n5XKVASktOYGVQ2h6uSsbtKodUFr/+M0dXA3zbzvh6Lm6YRCKKje0APQgtOQw\nNnenAEM6GfTHPoT7jIq0vk+VgAEIZYPEHtTJYBwdZ8Yyh6CQZaQsoh/RpNd/\nDX5E/KGPziGfDBjsalDST0KSMJpB35P7cQLtqrrU+WGavcA+U3OF+h+0nfcp\nhdXj2RDxug6SRlLB5wBeUvwQbomGeOYuHVUuDgTRLSt4HyNIGE+g+GSNAy7G\nTVE2mw6RfRYIac9sJ6yqsoSnp9xwrxGjUZq/O2BzFUj5X4ZqqIB8B5A6KGeB\nxWlTgf6HOuX3Uvh62yUWiqvR/c0XuBGkoMWNKF11IAgHtILqx85PaCtZ8/63\nf2lLHjHWB3c+tsXRX04+eQjSe1APTQ5xSnRwI7RVQfAPtOw4FgSTJ/gnZL0E\n1DckidGD5T8KTmtEp0SV65n9H1h5IZRQoAHnbNj48wVQgTnrTBk7cu5XMv1y\nbqgSsXipbXW6ioO9cz8HqhpQ9QpTgspYHoLt2G3U2itTfpjyDTa53uhKsXeI\nIGsei1yao4pRY9B/GKlvApKnPLCczjnMmACD9cGeZL1UOCDMc8H4gCYOKONd\nmR9ZG19DycVvXaIu2vG3f6t9XvJV6P86+MkBCAF0j9FhmSUWZKPi1OJ81YIw\n/bl1\r\n=sgtb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQPrYk2ZI+cQyzoRSL0xRJlVKcA24ZEZ/FCcnHgPvW6AIhAMcl91PIJNzg/ti9Iq8+wPCpSKMBML9JOEXkQcWfNpP7"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.1.7_1614780619934_0.20701886391804636"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0-beta.4":{"name":"@storyofams/next-api-decorators","version":"1.2.0-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<p align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n</p>\n\n<p align=\"center\">Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.</p>\n\n---\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true,\n\"emitDecoratorMetadata\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Post, Query, Body, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  public createUser(@Body() body: any) {\n    return DB.createUser(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                        |\n| ----------------------------------------- | -------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                 |\n| `@Post()`                                 | Marks the method as `POST` handler.                |\n| `@Put()`                                  | Marks the method as `PUT` handler.                 |\n| `@Delete()`                               | Marks the method as `DELETE` handler.              |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response.  |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.          |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                  | Remarks                                       |\n| ------------------ | -------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"560bbac6a5d858082f83b82b01629f717b28ed68","description":"<p align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\">@","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0-beta.4","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-j0MFkhabggnR6rdx8fTCrcexnF/GNic2V5sgPLTxueuf1M4ja0cmvVOOohbPoStaBSyW4Pc9SwkzdllcvsL1cw==","shasum":"8599807bef3ad05ddca60f707ff63752a39ed398","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0-beta.4.tgz","fileCount":52,"unpackedSize":120199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQPtECRA9TVsSAnZWagAAnLYP/3Z4NFnW3BtyySb8jfSK\n+m0tWpBDWQ2UNnDwDBUL2eSmY5GlaqnaurUckPmg5/xHfXLOYK2hxkXFpfF9\noYnayLqtmvqfQ9CMifV9TCP6tW1dPzWMD1Ud1NQoSPKAq1CC8xhZ7YjMTzXv\nwSrzWLHLn7W+O3F7BtAJIMlG/rzjfFxn3arnfZ4KzK+GP6GitiPL214fZ0wa\nxcWAX+00RtEdMShyvljX1V0cUhmyd2ZZC6KeXD/F8ggsKODvsCq0IXg9ISoz\n/q7CDGLJxqcLZBVwnM01nucXf4qDWhMpd7oYoZFqGc5l060zS/HhLnvdG9WC\ntwUPpOE4UhYrhdFrrctgUP1AnUKK1lkI1Z3bscAMKpyCqFCyaJyPi/zWc/wl\nbI8zKi99RHJLU37HuxDhaF9SYLkuQb+ETbSvmupyitd2UyMgCucffY6B7qJ4\nvbxl4d3Mv8FlEH3SOQxi5aROxMRB/o0hubj+591zBgKQtJXYiBEp+g4tQhjW\n9vMyIwRDKDWQHJJEFjvh3FDWqWrHWCann4sGOGBq8hmJ1iW+CrAJguO14yvw\nz2f5GajWhHlFEBogkcEUBnvWgZ8MiEiR2YckX8lKXzUWf0PYiJp3+iAjcDZ0\nuANTuVOcgPS+tmw5cgsn4dWjhxhg2Ut5JMFihoYcPocoIV74EvKBJ765u8jx\nsgGy\r\n=3scw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoHpGzTUKN7bdsNZvNZgKtnA1jYeQwP8jpejQsi9OQCgIgRT6L2ir8G5M+ruJULFVixtwFxPnS1GMY2metDTwxl0c="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0-beta.4_1614871363409_0.7747464302221065"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0-beta.5":{"name":"@storyofams/next-api-decorators","version":"1.2.0-beta.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/nextjs-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nCollection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the `plugins` section in your babel configuration file:\n```json\n\"babel-plugin-transform-typescript-metadata\",\n[\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n\"babel-plugin-parameter-decorator\",\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true\n```\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Post, Query, Body, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  public createUser(@Body() body: any) {\n    return DB.createUser(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\nimport { createHandler, Post, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  @Post()\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                       |\n| ------------------ | ------------------------------------------- | --------------------------------------------- |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood              |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values |\n\n\n## Exceptions\n\nThe following built-in exceptions are provided by this package:\n\n* `NotFoundException`\n* `BadRequestException`\n\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string) {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"e8469df22f77239812ded5d6c58f0682717ff4f6","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0-beta.5","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-Ojha6U7QDhYdriqAWjTIhCdtHrenrrKeejPaofs4VupIArKfOIJJ+EO4LxGitvflfIIN74u0nVRRO3UiAl5dtA==","shasum":"6d7e3c3576107affd82c8529061aa164d62e5ea8","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0-beta.5.tgz","fileCount":52,"unpackedSize":121161,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQPzoCRA9TVsSAnZWagAAgg4P/RkOgD1aq6VNMkhhRIcK\nmXGv8Bzde0ThQtAU1tf0uP/QzRlp8/0muRk16px7UVNkZoNF/l9cFdZZOtW5\nfoFSHXhdkuBmVq8juddMzQ/frhBXrR8h0rIKJ4ceYVbMfF5zvxBEKVy9m/ag\ngiBNQZM8Bqqx8afmkxwgnyOpVAtZ8EiZVrPLY2JmXvuyryAcvNow9h1KraDR\ngkQAG/xsvAkr2yB/+kw/iYkISv0Ls22ckgsaFOpWwWqxzA3Sg5YQ0Ayn73Df\nRcysHe7I0eM9hT8rFYCU7H2Lev3RUHIMHsCB4RaO4TtvvDvqdl3Pahk0/iq+\nY1JM/F1385KEyR0dVcmQqJn5jCqPYLd2A+EO7y9AABkINuzpNu0wrOo6EbKm\naWpMl1ikvmzULug/q5rCil02xUtsdx2pc4b+/CDAFw/eqZVpOae+NTK7rGY/\nhwEyk8p9q+dM61vSAkJv5UIlykpiQvP3mbHiSRXw85RT/ZSZL63KJJ+mVLVB\nNIadkU9NyxWAA0cw2IfK+1i3ShWpyU27i7fvS6Ya4UdlUdAEi8iTajsTTs9x\nNhCPc9VBkdD+EtxFdR4w2DsPwHLH1aNrtVK66lmeFWPtELoElnmo57LmFBXL\nL9O88USa6hEkYghJC0JJIVHY+9wBtrwtVvEnYr7FtlgZ6MFmM2kMi2+FGQno\nazsB\r\n=u4G9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGtwhlzvxG45TJSb0AekI8fMymDSp0xGSatULtcDcs6QIhAORFCKsTWSW0c1P6Az8bX8YHa3gafULyFl103WvnoKJO"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0-beta.5_1614871783632_0.11504998434704627"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.2.0":{"name":"@storyofams/next-api-decorators","version":"1.2.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"0f12eaa25d36961a4f851f9e5828c0c07e532561","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.2.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-0O2QPTgRbcPOMXchdHwqJgxyzqvzyzFCnaGWDYKgxu5f1f5ONMmdwkAcX3fG3ETCS3V8u65QYsRhnK9XCzRMtQ==","shasum":"2929b835e4d694ca04786de849129c74289e2eea","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.2.0.tgz","fileCount":52,"unpackedSize":121891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQP2UCRA9TVsSAnZWagAAJ60P/2yCOC37VsIoD3QcoF9K\niTSubPSKwL8L1SXu2eEikAmyPwq31cEBcYOtc05naahTQXQW+JFQd+2kB1iP\nW9TkC3RyHkBmCuxzcW28e5pVDDa493WKxYl+mIgd7iBRjQFb/B6KZaDJg9zT\nOlp50/Pq1q8ncc8AnhzWlZ+8XSozX7RmLrCHZ18gry+3R6kUKPY+uPE5PG+e\n9eMZBQkCXA942ExvXkR1I+SVlhowXN23HDugTl03uerv0gO4keIgl5SwuUhx\nz3aVVpnrYUaHQuVZGZplE+mKlcwt8bMaBMAUM/T5sD5umWRiSxUirUlvnw3W\nL6dU4cFHEOjfZWKD9nzA07ZgJnkF4U2GP53iiOXGS2k3c/VxNjAFPILxNBS2\nNSoY0kCA2MYGDnMx59c55mQERPAlwipVWz/z93NoKVexRPzBaM93pDCHkKBW\nKC0aJlW50OgNeSC5EaSbljVbad8l8XzsH3yIEVSkWgRxMsbuO5qnTNvbGZRa\nJBCGAqQbOAT/KKXh+9E0zFS8pbDUHGpZpw2s7t27f4liLuMLTbOe4pKp9Kcy\nkaKRZtdVdnFlwHTjuy7+zOl53DSBnDOEM1eck2bu1Vlvjj2MWIwjAO7CsEmq\nMgphZI1FvC0QsOsz4tVIY4OEHDlCF5R5hpV1sc7gH5UlCNE+Uwh0DZPoqDZP\nMAjC\r\n=ukaH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQMUGZ9YFubG+NbWJnCkM/hRdVpm+Bqpdgd7bD59ptcwIhAMEHXlG0GlZv1OU9DP86omXkdxXkA93wIDdo49ysmF51"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.2.0_1614871955729_0.7960939775664306"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/nextjs-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nCollection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the `plugins` section in your babel configuration file:\n```json\n\"babel-plugin-transform-typescript-metadata\",\n[\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n\"babel-plugin-parameter-decorator\",\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                           |\n| ------------------ | ------------------------------------------- | ------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings    |\n| `ParseEnumPipe`    | Validates and transforms `Enum` strings.    | Allows strings that are present in the given enum |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood                  |\n\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"0ca6370673539a6937822a8b99bf4262d0b8d1f8","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.1","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-WkSU+LT3Juy6gbD7XPmNI4uKBTL0HiL6JMJQ620lcHKfHMTlGrNjjJGwRGMqUsQgwUkGuuji+8dhMBZ8j+VMgw==","shasum":"4a33471de6d4a3af0abf60c98332fb1d43ddc8cf","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.1.tgz","fileCount":58,"unpackedSize":135736,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgQlRkCRA9TVsSAnZWagAAaQ0P/ikhWOBOjQSVUcR/F5ot\nZxOE43cvA/7bLiRXzoDmq2Mbna/nzbbI7J8eWOhb2oZ01hN3ATKUH3Aaj7bo\nZagFJ6EVHUiiT8e7u3YqhPEalgL7CTmVe6XrY55FrMC9I3qqHadtAMxdnrO6\ndIniKXEgtIcxx8OHJSnZqc97w2s4vmqTmIHUCQFeLX9YVyyvqj57sKR9HlvF\nNDcOeZmgzj1voDiN9VtykdDrsordv74w1lGdt3Uek2bPPNFg9k17m/zKNE4m\nrXidVRk57xJxSxmZE8zVWdCbTGyOAYkziVThdwuTcdltYBZC8lTWxVYktk1N\nI4MOfMfRR/h9ddq/xArUsDbLhkabPvoVX0y0LPO8PHQvXRH5/o5z6YBRdXXA\nmqu3bEtpMCLj6vXUJAoz5Akygfbm3Jf30VNgXlwGbljmjOYPjpzmv024UqJO\n5Q8zxCDBLlfBUdBllCqm4y8N/cStau/HVPxEzNuvnWkKbfq3fOqC+g84o8Zi\nFD0hl6124yU+ynbhEYo6xodJ2QDDI3+8jowe2N7B4QC7Xur0F39/6jq+mjcw\nkZynIZO8it+LcuC6n9FyV46yvI44bpduD9Zpx4V4Qu/zg3nOeg0zHmVWOpEn\nIeElUnhWlqtAi8UDZILG6mpKAFCDDPj88xGBwYUNKrQtOfXu9JSS5+9WRXU1\nuxb3\r\n=TBJ5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFu3Xvhy8KFbU51sfBAp/pWg+aRNTJMb5LhpyqcCxVWcAiAjBYvdqmjjjHQFj0Pk43tD7uliQ9QpC0ZyfTnMdK1oEQ=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.1_1614959716445_0.1201450072520609"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.2":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/nextjs-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nCollection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the `plugins` section in your babel configuration file:\n```json\n\"babel-plugin-transform-typescript-metadata\",\n[\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n\"babel-plugin-parameter-decorator\",\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* When using `@Res()`, you are in charge of sending the response to the client. Therefore, the return statement won't be handled by this package and the response won't be served.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"81eb8b18c960cca2c29be2c0832dd38871b09aa9","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.2","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-EZkpI/Hn1Jk7aPxTlwXfzKx164PgYS57tkQsa+CudcLCYzTignFFM49cC5N/jyQfle8bBllz7LReAGTqOvUXAg==","shasum":"d176f90cc67134d5690838ed885fcdc454d99719","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.2.tgz","fileCount":62,"unpackedSize":136171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgR0kZCRA9TVsSAnZWagAA/LoP+QGBG5Eur49yCKsn5UMU\nE0oun7FwEvr0tSM7TV7iBPAiXtWP6HSSsGyQDY4xims1CQnPMH1NS21xmdBD\nMwKfF2O6CuqdvHApTJdUww4bdW2sPyCxfSprqobIhw/R74oFOmZRBBOGYhOQ\nSjHZ4aDc8aLUo0/exYUkOmsZ9/JGDEHUgVRihuRk2ZTjluGcGwb0itYmjK20\nOmpG6661ShYbul8FeB/Nto4a/0VKTliJ08IlEmoMFijv4frI9DNw4OA+WGk/\n8dZQStVBc+99dQ7kTJqO+tt9vgmgm6/Z5GhFiXfcBMZUkUTr4vKT4HPRgR+W\nxpkpxZBgBCmGIZph/4yQCVFmvMy/klBdcJdUahmurvA5FrxtFSKrURnDq6hn\nxg5+BTTuRgLBpDwZAE7v3b3TH8OKvB8MEZzK+dpNo/IBpxdgBowBryBT/En9\n10BQfCwlqHd7NkFqpa0wCu8cwDza+QZiPFGNgXO7afohgy9F2jjR33BqD1lv\n/8x28uDaiJ1yi5fPSqFpGlnc92KGhYoP9GaBvhufws6l4y6C6g0LdpCJ+XdE\nTH8npG/ozixtZ+xzG6qnAurnIZQ+rfyhK9JoTDloukBSJ3AJVbfgkhdoxodZ\n7WXiw0GwyCyAZnFODgK5MhYhIhC0WoxERSJT0xyeStGpMsXHRXoHQ4CMPwGp\nLDRr\r\n=Lrhv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHsu+6CSVfDG/Mz5DgxKVZkwWm3CApU67uAhCp4yGxrSAiEAuRqazo6dXiC2C7ud6rBSselwrkbbdGEMktN81sORj8k="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.2_1615284505076_0.46810508145813157"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.3":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/nextjs-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"16d25a21ce0bf5d63f657229e160a5b04c28ec09","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.3","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-Gofj4FiPmupNkdrvCz9hk29ISTA/DYrzdE8lnfK5l9fRJWGCT93emQaVJVanuogEniIjKDi5h7f+YBipTXJJ4A==","shasum":"d61ef3f95e1f1ce4e5d15edef5aa4e646c5dcb81","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.3.tgz","fileCount":64,"unpackedSize":139619,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTOBjCRA9TVsSAnZWagAAyOkP+wTKLdguoiqkRQsRswHU\n+ABSKieIWOM94NiQy94L7X/s+KfuoLkK22+TKxdJGVaxnHEjgREafTudkleP\nzRvbJMVnrKEQ4w4al0ObZiD+T0dWvxKENAFhIc0vMeKCZnr1bTRFZsagzqsJ\nR3PW5WTb06tP+PF/EsO/T9/D9GBk0OI9LclQdW9Jd2cWqJgvcdnAD9KyXrns\n88T0QIvd/ZUDIkp7gJ/XslnS35BJjIaOzLM3d0WgQIKr34zno0j6vsp2FQ3K\nOPn+ktkGYoi7THpyDjRivNQS+M2cPMWwvKhFNF+BDLD5CAGdv2BFi9jiDK+N\nUVSA0iY2asD+Wz1E5Z2ZEimRVAxi3WD+IcAHH0oGXf/hsZOFWqdAkxl96hEn\nrDaPzG6CRp9EbFyRvjeSvZadWXwixXXNh4SplBq0YN3b+2OoGgWXq1QfTqEB\nf5hLCz4eguBRSBXo06tzo/IuX1x7gJttsOX97WEihP0ja/n+CYM5+8+TG5te\niSkxeRVhOFWk296TIfzOrJSebWV9t5UpzCrlj00MBXN7JCRKE6okvN37hSXq\nd39ty6AOZlx4i29cqCi/3Gj0D73W3XM9fyeg4zIBG0DhNniTwGa5C3LNMi3a\ntdYmMnl4pNIvYTKlSxjmTTmyqsEXeU0iPMmIVVLRXE7/RjHyEcvo19DvK9f8\nJyrs\r\n=1Xhe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRjWCZXgDOKzghgQ0G2iInLeF67Ci2PAqOID4OQr/K3QIgKKddpbRqMpMXsH4W9qAdJsh3R5xgahoni9i8R7y4vy8="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.3_1615650915084_0.005908125552309462"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.4":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"2b9d8336bd9b70b89b501e585497456889ab2c33","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.4","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-F5RaYysip3DPIWOwDjgAhVyD4PN+I3Axu9EyDvadiL+tb26xlArKfS2jWroP3j7c2lVykJpkOtWDt0uZrS0IpA==","shasum":"4a3a4dacd5fdd0a3aa71784a128f62a7563f8eec","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.4.tgz","fileCount":64,"unpackedSize":139768,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTOIUCRA9TVsSAnZWagAAHaYP/RSRtaxb10WeFTfGv5Kq\nozCBSX4r94diRw/gCpJy245aBMnosMPuPPCg0r9UG6ND3SWwquJ3sA2hUKHo\n0w8tG8spvai2hLmjyn0JdzaWHxQcM1hBGobKEb513mcxiYWJod7aRSbr3clw\neQsRKyJzOKBZCQF9YgqgoK/Gpu06L60A4kWUVICmIv8i70x0n7bHgnADMJ+O\n+qqFc2DzepAbJMOV2Tvu6p329w1NSFHRqyRGRidevd50kzM0xzFPoN99E21l\nqT8jo2l9u0NOGHP91JktRBNzNBDGFmSOmFZIEYJLVHSJfHltHXmbPcKgdS8t\nbofhLVQTFoNIo0a04DUAmMzlxcyDx87kCzcupjznygoPCoZq3zmU1hOZhafw\nOLZ1Z7xVmIoQQF9K2iIC0XF1nlxe3IW+9g1+uQiA0Lup0CspVEx/o+srAwk+\nxQkZ6m8NCMRRwxEeoJyMIA6bl63AhXSkuJ8jbOlDoYmt4D0uUHbC7KHieYKY\nFuAUqQkS153uW/IZtvStMjeo7dhybKHWEppWHY9B1cyFsdOCXYSinVdBvYvA\n8wDMgog7bj1DAkxL/YOG9o7icXmeu8xV2lvaMkjR82kQ7tIAqaYldZx5SG6V\nsoWSOpEJD8+OchSjxtVN/bbp9kDOfsUGx/KISw9Cjcj2MtJ/PvExkF8WlP/d\n5YiD\r\n=cBD1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAkY7aQjC9tMyj9TVonh8D+mBg76GVEUy4MwT6zYGwiLAiAIT/I4wZYVei7X3woi1fmP8m01bt66puvYoOT5lUg38A=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.4_1615651348302_0.9941178123182424"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0":{"name":"@storyofams/next-api-decorators","version":"1.3.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"62a24f88f1e4b47bb11e8cd582580d25fbd8ce76","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-5Atv+WErOpUoZNipmB6U9EtUsV6gqJmnRetKL2M7mOOGbHpIc0mYdqxcJ7b8Kq/I/qnXLOD7wR3ska+i9DntHA==","shasum":"837cf3e21694c689c68f0d9b455a1dd812ed7068","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0.tgz","fileCount":64,"unpackedSize":140250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTOQZCRA9TVsSAnZWagAA0PUP/1y8jtEgKFNp38Bl1xKz\nJMJKZSmU4jihe/9fmPli1M1fbq1QFsIdA49QBUF9ujA1kWXKRVMiSmEEPm16\nKu/TvYAqCNEv7XmGSSULNY9NdKCxVwCrUOm72KTxs2e+nETMjPOk4/FZk/kf\ne9Ol4y7BzNWBz+X38K7awl606bv045UdRY78Z1o3tosyb+fFwFprDQbNsBgQ\nXgKhVzy6kmeZulrrm3wsX9+N0CTYwI216R6EdxS1+Jib2yh8FtWQz0wSOwGg\n+Sl1LnzX4//SDnE2EQXHPIRiy7KD4tO4NW7nGsxQM6BhKibRzhV32KzM/uIo\ny8dBL2SGb4awBTo3n7O3gWdn3gM5H8mGX5EqMTHw7G5Jxvnhm71N/tvQ6RJR\nO9D36uvZXrePuNd3xCG/qmdHPl/lEeTGrDCQTqe872kFaJL8fLQuft6rBFxC\nFYtpyDVMwvEbYybifXyouCY2SCbvcPEUfyy2HEJlzOB9rnn7pPyeg8dz7OM7\nbSC4Nbn1Mc6/EKbjjxFXxjnb9MAM33b2cFMcdfxroEIT/LXatSt7MXCNeOgR\nj1LAONRDwb+SLaOzuzSCTeOr7ijm7vrRMFVE5c/Ipa27386v86y9NJes+abY\nIWjhQK9eMRfxRv6mf54YxDI6PG7N77b2ntYeoj4WMr1SbNomYANhMGGX0A+p\np7Ps\r\n=dB2X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjVfmDc9S7XwlApU3UjDTC0aJ5o7AEcf65w3+v3zGJDAiEAuj2h+gba2I5e/Q0bFc5FXRVeBSAB8xttu+3ViOQkAO8="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0_1615651864524_0.04350636123012608"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.5":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.8","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.3","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"0887d67b8f962de218dc3cc4527e1915b71f23bd","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.5","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-PD7dicgLh+v/rmCXun+jOKNH7ksZOl6n3kkd+sDkai7Gao2cnaXUXrQVluJnfuRA5XJdbb+lxJCybo1XEqKJVA==","shasum":"417b43e7ebd8057f06f4271cef8f92a1cb8c0476","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.5.tgz","fileCount":64,"unpackedSize":139618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTQijCRA9TVsSAnZWagAAT6YP/3nr6WjD/5ZYWb0yaNvY\nc4SXO6qm85A/DgXauKZtq+cXj/MMnAAHqnG0IJadOPffTXcs8iNaA/BaLxyp\nPyGkXbjou1Aj5TF1jSDSk8x/c7fuicj8chUuUJy4vp0nWUNp7f0vS1RCo697\neJIQcC18SJ3korXpeAmcLD9w+eq08q48dMHIgj8pN7t78a3PK7OAQ4+FvIaq\nkmfce+YguXc3elMFbm24fZZj49JI+VIEZLRkkFm13bRZ5QafeqWc5LvlQ6OC\nWK1fftMmATJxfCs7hSPY5aOl0Zg6BmEoOojMyuOhlylXpECX96fwjxDgADwd\njtq+YcAHHYk7ilf8/EMQ88OiNH00ebo8szt4hduv0KUzde/GLCb2jlt1VwrM\ndEvzCcq62apQfDboyTcA+Le4iRJ6l0exssT2e4JAcU8+VamLwfiTqihem56Q\nHcBC36IOsVK22A03PX+808JOziy22hrG9up+vTFeA/RAYncEYV2fK0RZuVVX\nAH/N+TPnuReCaWqRyo4ZK2O/4KuVSykDYYAIjNZ4x42pEusEW4ntbb8XQ5wJ\nqGbr41kd+jqK5SUKCA09apR6O+MWG/pO8V0d49aBE/crE6M4eWwfGv44PKUG\n89XMVgJa4g1ykBiD8MAR48aE3Ah3Xy8rmVbRPG3dzPt7UbmyVA/3wmiWX+2s\nkHhN\r\n=wk/P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBh+ick0PYGaqYzVj4fpthmOZ+Psbw1DKVa9v/6VShwfAiEAqU4lWSe1ZFLQp6CKd6ftmmGyGVSIV8Yf0fB5DKK8MOk="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.5_1615661218922_0.08447924199667534"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.0-beta.6":{"name":"@storyofams/next-api-decorators","version":"1.3.0-beta.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.8","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.3","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"1b27df77d077ddfb3203524b20c654ac80338d35","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.0-beta.6","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-iNlAEmcpeIxba65wJ+yru2RHC1Zo70euN2j5zUWwARyHCWZoMN7hGFDc8fcRs3xPeIWKhESZy+SFX9kNQzs5EQ==","shasum":"1ebb41b8ad988176a2215e0ac4be0078b0a8d176","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.0-beta.6.tgz","fileCount":64,"unpackedSize":139618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUPFPCRA9TVsSAnZWagAAt1MQAJrHox+GsT0QBREVC0sU\nsRVSjYUsNyVmPJgFuwIJh32196b+xG4pK+nZpOsJIY0Tqg66kQvib4nhksgG\nUiSounP6jWq0OlDrl5wjAqJCS827K9r77oFGCkwLz+KGpNZmHcVzG1nxGgng\nDjuuYP7sk7dkNg5Y/ULstQ/TVS1ED6QR83IjpOM+y5+6Dy3PwtAVWvJMHTPb\nA/y2lyWb/HBR5H72k605wBen25D0quqv3Q5m+vwvlho4F/sT3UkAXh2fBXGY\nT8m2CTw9pylqfOsX+dBSNaub9/kpt3AzEK8cgIIh6TEosSN/atjtbU4FZd4v\nlbG4eu7g9vklYTQCEaKX6B7XCMzpRNd9I/pxDhrtGibzg01VToU2reVLNgTY\nfu3wHLEWBQPbj5rp+Hu4vZ903YZqLAs+m6WubrjDP9lgFN/RqT78WudsJkUV\ndHV+HCBzYweEkM3Pd0hDwmoB5+W9wQBi7FLugMg+aBO+YwdUn6lCE1zyRQzt\nk4p7sm5mZkpyL2nfNdYv9gcx6MWABtoEbz0l50q2ssevFwCUS8g5IxyGILvF\n3Xim20qfUvqt7McKSSH5nvWgixtIrt6FMeefIgqaIyGFD+YBYsTHgr4hmWDg\ncMcW+nhz5q0uZEMihEKvocwUzW78S0dvSFTUMoD2UcCONer+XxPcRaBBegoj\nYb4j\r\n=lnw/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/0MZdUDSCKSo9FlcuxpH2g7MVZI1CawxXVGabxDovxwIgEWWmsM3l50EXZzVCPjE0PVI0xoBsILOwmSbAnNDlh6k="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.0-beta.6_1615917391019_0.9362686333761852"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.1-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.3.1-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.3","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"79f2fadf947dc49556e47aa44105c2a0ddbd3817","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.1-beta.1","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-1cUynSHHFSGdFCQu6tIoN1IoUUfpawByNJeQoUOt+NNjlGGlS/Kz9a+4IurkvrYFn23aQto8OdeidYNj4tRc6A==","shasum":"d6b1bf8d03a78295ef61b969593d39d024040f2c","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.1-beta.1.tgz","fileCount":64,"unpackedSize":139613,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUlKrCRA9TVsSAnZWagAAHtYP/R8YejldyxkUfSilBC2+\nrTVxATdtECcoweDo2ZbYWERaVui1ozHjx/v/MdzV+6KitOGzGJfDSFmmX5ux\nddGFYOIW7isy/ZFB2GDWlBodXMYUe4+R0V8abxd4+RvEKIXspMUSMbDkVcot\nv/UYhmcbpQCHOoz4Fkotb63J9ngusPipvtqHDcCtvGkPLEUNvczEJaIqRYKC\nWz49lr7nRSl/Nt65rqbWjNvKW+0ZMonTRDvxHJWzt2FMeZhzof/UznxqepwG\ntvLZ/F7GdDeXhfP8x9KS1StBWB6NeFdS7aUq2D5oX6lqhK5vtbs6nRfnjgys\nUuxnaY5XJ6IASjbmVGOzGKPOGkifEdJwOMtJqHLP8mCYlxFI+66r/S841M8P\nkEoMUvD5gCYG9TeJHlGcj7I3xUtmVzMZNTOhqjFGZBjMdTjvmzQDyfJ/aUD3\nbrJCUs8BwBVPFC/7ZphJ/oDEKRwTL4pdYue9uoG0uC4c2FZmq8lx/HUke77a\na5/y63tYFveeCtBjgsbSCZdxjQqCtH+ToRRPnKVZPVHzk6oD9UW/XfTMIqfi\nvrI02SDK49c8VQMdVhy0ocY9mSvhV8xID46a06nPhAh+UwUhO/uK3OxIp/Yu\nPjqyPKl0q70YD8O8eTyc1FiRjOYLQsEklqKjcfff5TtdgO0uWZ5EkF6Dpte9\nHoHL\r\n=ah9R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHIwt3hg8o+jN8R2GN5lInIoVDsti0JYm/P3+ZWRv8v2AiEAtdzZaGUzUwuX5qMSgDWieJZROD+MCNI88lR60KrO9l4="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.1-beta.1_1616007850652_0.9781704833140399"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.1-beta.2":{"name":"@storyofams/next-api-decorators","version":"1.3.1-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.3","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"c6aeb704fba0db67bbaa2b72867a16d44409855f","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.1-beta.2","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-CZ7Ig+TN+YiVnc0ho8tikJUKkUOrO4zPTCqZdwQqXT44EHujUKPBhIjTk3h0mjU+MsR3EcSBTy0gFsVOUYnaZg==","shasum":"9337fee6d6dc39bc709390c8215377be9160887c","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.1-beta.2.tgz","fileCount":64,"unpackedSize":139620,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVHU+CRA9TVsSAnZWagAArW0P/3AKmV7KPo305pZU/J3t\nPzbsQYwM0PP/47bmeJhWeLfncroIbHnYpZmqOO4MG13wZhqTvxl6JGVU8Wzf\nfQBDZfOdYmXGzPV8Q/9jqDyDDMtl40DGOyt0w8ESg5VAN0TijojPOg44SkA3\nr9I/xMv1Wti7ADMzwIWuvbvsfEu/9ceKSN746sxXn/aZnasgZAOo+2ueCSfp\nj6iBs7mdihDA7gXBsqkZa+gmSH+NpXxtzTa+QQZLEWxcqBGrRUIAeddPBKIb\nrX6Gtfo9tkEg8W5yX86wr9UhXH2tvN5ntkJwUoazGfyrLj2MJPlEsoxrtu5Y\ntWYQ7m0KB4fMoYmvgHVjT2E7V3SRwBDMlasvRKczrNQn6rSBNIDWmJOj2BqM\ncFrou4Ydn4MJo3hTMRZtRiGGz2coY4yeOWxJJgzZRYbFl1B60WLkXVGPRQzZ\ngHthXWXqebmOpzXTn6SWHh6xhisU3slf6Ss3JGde40tIRyx2uhuQv3naiEAi\ng2AnP0tzHPBEBsJMDAv3qeeMuugSUx1fIqkh/5Y5W3J74k6PwHXCNfSeuvEv\nVwmSksBgZyzjnmjHZvKYDCpPK+9KTPxQx56DG/DWRXPRXH+GDRl7IUm9xB6n\nlVLgo7V/QFrDTvbH+w/XWXcYNkKeLF89TfGsJg6923ePCjLcN641tTv8qQbL\nnc1b\r\n=yABE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE6cHrZNhOucqpYm5NLnE6Ai6eZyp1EOGb5TJuTjvi87AiEA8FND/PkxYR9a9+bsMWWTiSmOE9l0a28fL+A/dTfEJhk="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.1-beta.2_1616147773964_0.3788728716386338"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.1-beta.3":{"name":"@storyofams/next-api-decorators","version":"1.3.1-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.3","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"de81487f55b26936b23e7057c2d625e9d23fa518","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.1-beta.3","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-hG0jyV3hgSZ5nIYPFzlK+5GB4OXOvZLBz6QBzOitz+OmgGlPR7KswlWMKRqxs4lHFgLcJqQIaV4Dh3cCo7FdeA==","shasum":"9f349627ef6fc3264aaa88183ccff0873e04fe48","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.1-beta.3.tgz","fileCount":64,"unpackedSize":139650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVHc5CRA9TVsSAnZWagAAKz0P/1Biot9PeFB/e0VNxXcY\np9/TCVWqtGdmHRR7mD/VcGoFWtQJZJfnLfxV5mYDm98e00klx+NE7oUtscjy\nl0PmYG4lk9SQTUB9fugHNbrrLMADc4dJ+DnpNT8mp8TfktEd5umzrsQZOODN\nT5kxNaMt/w/NfY1+z5nanwpdhTpLxraE9x/G8UkXYBqbStPyGR6qIG/eTExp\nA8y1GHM+VsCv6JQBp/U2Oofo2rSwGsYWX9cMGKdtDtONBAGrf859v6sohZAi\nwxMksrZQap7G+O/yyG6deOD9Z0Qje3gI2b2pdsUByrhbo+SH5jam4Z014YGJ\neyy9EcmUQzfrc5SdsyK/jc+RA1DkGOD755Zyvz4Yu6/ikYd39b9UTHuyWPnx\nkiLewBJPWgOQkMYG7iOyQBUiclJX4NN12VE8wtmTSaPTjxro/vtoKNtW7pXS\nLt0vNk09ZP22KZBpEturlqiQSDQPIhmA21UVhEAWpDB4Yq/cvPO7yJWmkr/G\nkfumMDW7D/Z0IPwAH9iw9IgIfkAaqF90G4+jL2+2FgN6m+sDMpXcRqgMpeAP\n7sa6oUwzVht+yzDJTFiCWIpHngdlHQC+9cQ4NET0RFfeTGNilq+gJ+YiAYsF\n1Gr9uvwxiVVva5Pm5UC+kO+fJFjLmKFnLX9xwlq/EeK5M+5ziJZtAfhQxEYi\njQ4G\r\n=QaJk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNbZ+CftokoZnPmlA4NCNxMLXUpoXJL6YFTzt7ylllTQIhAIMPoI5p+2NsCMT+0MNHyi0I+CP5DH1L907/hKXLzKdz"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.1-beta.3_1616148280529_0.836172346731878"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.1-beta.4":{"name":"@storyofams/next-api-decorators","version":"1.3.1-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"1648c30ceda8237317a4f33701809ec8e83bb702","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.1-beta.4","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-02J2PUvUfkuGvhqyYGrt0hca67ztRHLzejUG3pm13UPADTe0sm3Zxh4+5eEhOEv4VYs6sXmaUSniStGYkRKRnQ==","shasum":"b5138587ff846e9688a592326d9fe2577447f114","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.1-beta.4.tgz","fileCount":64,"unpackedSize":139650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVH/rCRA9TVsSAnZWagAAHR0P/0SINa3v00mNDj72sefo\nr96RViE/vV3tBtf7aYV+FpmmT90CmYLBCgErwpghZnEanTT8KHBs32Kz2apQ\nHtoFyMlkddVItioBAJby/Z6+sC8J/M/8rTrTrzxFejezc6T8CZRLoNvmj/BQ\noK+3oJSB40VqzTrFGiacTfmz+akSKSCr2+if0QCy51Gu+RT/Hl/GRVw7IfMh\nIOFxxbq2yTa7NkKge6h/rQr6wdzfSxYDQceqYpzh1J1e7lwCtaEk1GBDuqbH\nOAihzSatK7Ia+APaNDoPBm6KMPzAz6B7/MvtWsdzKtk+xb6/mZLrIMezlUyc\n9FhsjJ1uvuCZaRoiMNM+E4n06WyeWYI06XjOvnSyxwn1gqtQ/faohxP0LOxm\n6WM9WRyiVneQDt/GRITZpMe44GWfKs01uafUoGyitJvDo2wqF0HX3vMHUJHN\n6l+kYAg6JzTYKMM9ijimy3d/hirP3I+jS2IZpoxCa0IQvRqFg0J89x8AS6yR\nkKNgq2mphskJefkcVORFYXNurYoIMtbx3jiYL5ZBYQUjT608sSYcd6YiV7GI\nzx5bSDf75iK4kuz+bUgLlLOoCpOGRekSMGk1W/+fweeGO4yKkn+cnBo7mnxm\nx6DlmDSyQwaDAp1YAWuvGFDJbMXiI8/dxTRXjEHJHj3tcGdgdt3V42wKwH5D\njuhL\r\n=/0Wc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICvuHC+JN3RJCZlGHLyGEADMsOLvdoJ9trM5tqBumfpMAiA/tky10BaOIAkqDuNXhmkw68Bhe3q01oMBB0YCCPglpQ=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.1-beta.4_1616150507057_0.7160320101563598"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.1":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"42e81ee6097b9a623ebdd8161b8860ef4cf67cad","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.1","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-m2l84fK8a657vx17awrZrc82ELKNOLa/XwapdK7PngjSn1+civNsyc09S6xb4Ao9io2b0dnT+XgcLufdztH/Qw==","shasum":"adb07614a750131cb0315ce3acf964ab775b3f48","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.1.tgz","fileCount":70,"unpackedSize":147692,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVI1eCRA9TVsSAnZWagAAmpYQAJknmQkkeNphIOsqzE2y\ng7sH7+fdtyYLbjNEn/C5e7bWvHkTWMusFl1Q+466TstHMjsxW4GM2tSDY6Y2\nJtI3joSQ+7If1J0AhzwB3pZiM9cXI3UHaQEa0aDFbhBq0Zry6IKKg6hwAeG9\n4kWIPS3ZCOlkRKF2t6pn7o6KONWl3S94FpiZ4pBU9cUqnqQCHOZp+iHhXTUg\n2oGS+9zkjUzc3GlXzwQZK7a76gi8uPMa/Jd+Qce+YqRh1Ac1TFOq0VAFiz4W\n6cxiI6L0724TO7479EcDgMt+AMR1eqOtJ+NRqBhh5pLYpPIzx8oH8nJiKgci\nCGLjfmu0fLkzfgUC3kIJe/mQdyETgr44iV1EHG0ylp+GWkL2ATo46iEmPK/6\nuhaO5l2NrayDbiKz3KUgcc3deE4YBeZjfshxi1cfjQp1a7JgbWEWd2pVxtn2\nf38qVvyYLEtRo4vOxorKSyu2SsdLod57mv34lUViSnaSlzbxbS2y2a6TaZcO\nfQ1zkKEwkiXVEqBgFM2PgrwDg/jWUwWQuJwKpVaBOdv7WKxPjcidfsWM+zSY\nH72GaKvuI9wCVWOx5Cwq1L+LkUZDZgA7KEKkdqsVEXZ2e+aGjx2asr9iKs09\nAs0/giYRo7cJEasnB3+SQV0oXARF84jAVgc0tcjCUs9xeo8yxvep7lZQN4Vv\n4jvv\r\n=qJhp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFfzaXsUKTBC9kxhA2KNu2ETdUKpB1XIbod0MlU5fQRbAiEAsvoJWArWhuHIzYuK4WJIquWWnT6HYcdxxxDJcfdGQNA="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.1_1616153949966_0.2778010665472983"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.2":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get()`                                  | Marks the method as `GET` handler.                |\n| `@Post()`                                 | Marks the method as `POST` handler.               |\n| `@Put()`                                  | Marks the method as `PUT` handler.                |\n| `@Delete()`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"7199737b17adbe4315029f266bc59182ca5d2e34","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.2","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-UYHIdTKfseaa+ZKzTYM0c6lCPei2l6HIkA77lxEQ0rfdalLliK2zNsceMKHf+GSqDjGBHvrFomW6InrFlhrH2w==","shasum":"9e84538a9865e1a0ed85f434e469e0ed860d15a4","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.2.tgz","fileCount":74,"unpackedSize":150511,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVKdqCRA9TVsSAnZWagAASiMP/1mV6XpLSqXMydzrHcYS\npylXxTRIPN/h9eU2627rAG+PP//nOplswZM6GsRRAgTS9wezpMwDnIYRSyUP\n6R2P6bvut3Ju+w0Lc4jpSXGtZVlfuEd6bv2ndf8ReF+QddLh8QhfeneqAdRS\nmSMAV3mdU9wrlfZTkwZCRD9YRn9qalhO+8dJ8lAAPl3VcsmbjSEIC4b7WVRv\nlUyIGD8jKyLuzMZGHBdvs0J4HXtdyJJ/1Oe5W8yBdKIcFmE8CRH7H6SeDqua\nWQ3cSOhF/VSUC9si+s20SXpR5FUcutwgGdrI+YtfMNTQLn1o2wkwP2PcWZDs\nBBubNDrhf9cKOOCTWy7AMQOsxVATeH24y2PLztSj53Tuvj9xrZnh/Co57Kn0\nFbjyfmP/NQq8GI4C1EEQ/MuZTENPV7DRLySboeNBpQbSXSxi4UaiL0o6XCuM\nAEGFuvPNpz+46jaaPpqICJ5pFrotZZhY+LDuiJbFehSnT8zzb7XjJsK4UrMM\nH+DeUr8PHfkO7jVbfT8ex3JR6jYz/ISA8PQhmTMcDkex6co/X6UMs39DGKrU\nD1GN7TxNWfShoaDSUsjAG5XD2tG/z1uPDTcdzP00WKeg4G8eN73dME0QU/Ng\nyW4Z5wGrxx0jgY+AkV1pIsou6Hefb4sc5qjMiRKpbQYFwdmSGGm0Kiy5a117\n9Y6v\r\n=9OEu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGjlcQYgJ3MtPyL2wnlEJXuttjeqisNtahygthw40EsRAiAmem7oVySFrXtA20PycH5rPqayVoXjOzDlX73dF8hy5w=="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.2_1616160618118_0.6889494840748684"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.3":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.9"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.18.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.4","next":"10.0.9","path-to-regexp":"^6.2.0","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"729643fa557720a48e20518ebba6300b4d374cb2","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.3","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-6IUNJkomJrwb6byfNYKn2EqVy1t5sBX9lrGR2zqxgMNbsyibM6dhIj7x1cYGM+sjEbNYzvo9rqV22cwCK42MHw==","shasum":"727df12285f3d8c63a8524b8705af3abedd526ec","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.3.tgz","fileCount":80,"unpackedSize":166352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWFSwCRA9TVsSAnZWagAAjYAP/1MJN0lWsKwa5jmMNQuN\n/rOIiIzzFP1DCZ0LuFOhVEcw3C4R0mHVbGVaBUPhe0hOEnPoFKWu1uxS+RxQ\nKHCgaPG8keCxL9WF4fsJqG398WbmG2TrTtiNHsxuGbE7A/WTWjovYgtVBf9s\nUO019g9MrYBvjBtdROKudkMfN8/VTFIIBtNmEFGp/VnhyFN4daHnl6Fa/RdE\nfc0qGeMPdlkvFKZFxu6AEcmBVsKLSCScLA99ZFgQoZwIxWUIJQGmWQYz9UEf\nK1ZgXrPIPtpM005ODYO+0wJnPa+5+CzYg8eWsy51fv3/s6kEsSPXYWCk9FXv\npPw1V80DG80tG42fBtx7Z1eCqUSjtxggZX77IoVv/RXlgIe7UO9B8uI2e87n\nIEjm97bLhWDR+JDxXnv62V+He2xWyYbzw0qJivMdpYUU+3efpWrHjnFsbZSY\n5fVim3wey33uuwiNdp41iAv2KtEOolyvsl/tZ3K8vRjc1BjSmp/cqcToAm/F\n0Xw0wyy442jGuZ4vkWX9+cfhPpEYkS7R9SoLSVJb7MzlVIcSq0vBRv3JQKRq\nFhfyww/0TX8YVsaeeukmq6zPimWxMM7jEWv45NLwMCu+X6q3rgi3JCF8Tv/Y\nBaa5dOvAzNZCGjDC5X/RAJUIuX2ql9ZkioK59+LwUCqH7rjv+2d6eC4cMAwH\nwOUB\r\n=gcSq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHhKQwkzvOQEXqtflzL141l1I03Gl1JkJb4319YI18SqAiEAnBjB4CRjkK6hGLkr2ThzxMUe5Z/xRTferoL1D1Zp6dg="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.3_1616401583666_0.7595929039665827"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.4":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.1"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"964641a5eab16e1efc9d60d7bdd30897aaa07718","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.4","_nodeVersion":"12.21.0","_npmVersion":"7.7.6","dist":{"integrity":"sha512-dN8xR/GxjwrU+fmWyoEhdaL0q/jt/3CFEbdQGD7J5XNS9W8dooQmxgvBxIayDcVNsKhmqp5hSJ4lzZA2KdpJiA==","shasum":"6e9a77153c783e7c608f948c54b6f25c93bc8498","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.4.tgz","fileCount":80,"unpackedSize":165619,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZKYgCRA9TVsSAnZWagAApYwP/098Qv/DCl/lTHIM4XO/\nlsTIpv+CZHTiyxnIEkwkph70eg/WWdXeEdver8Hw4C62viIFcCaNMh0zYZDx\nKlIkmOUU0auz57O0HhWuvcy6nm8U4zhYwJWScXZtFtgmJoQMhUHUAcCQ1IBh\nioeAKYCMTcglxt/xkIpC22U2JMyuCORT/52m+SF5HVPFj3wzkreDxEIQTuu1\nCx9HpXn8YoEk2Au+3J3GtFVjYjaXXKUkOQSneP4dp7Qui34+fRDrCAp7wZY6\nYW/tlopQ4xASt2uTn9cmMdb8sl1JLzyfDJ9/tTbdGCoR1lmWixkhwYmTdyj1\nwX/DWp0rrr0G8PfBp8RVYOwgoxZCUUM2Rcz/5KLPtEjfNvweS17lPVGocaFC\nhE+Vjn2ovjg5oF1cNQtdr3jw27ym02VLQ/+f8yJBoSlT4p4vJuYLlDf0pgIo\nX8RWByU9VhLfJDRcsCsjh6Jfa9sVijDNCp1ZBaZ+qTwg0HmJW5cc/rdC960O\nvTJwu02DEZh/v5F89uczTRcQ/mgMG+mb3XkbmydvOibDJSPQujFEF1BKu6PL\nzZpFLvaR3OKS5Nq94Yk9Clvf1OyFfYoigdTdIG1Sa6nPHnz11UPFXc9fnR5O\nji4wWBjhCvdiWgB8ScwtNsCYlhSpxwNunZTYeXkLijt2m69ZTPZVYTaC3QKN\nT2Rl\r\n=UtiB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+KWHdkjd4UzNsRvianwuOPEJEJurNY94U9cbkM3tsbwIgBFlET5Jfy70wza6ADXIOYTCYdDjPgdoxKRv3Hl+zxXw="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.4_1617208864333_0.6859352242736365"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.5":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.2"},"devDependencies":{"@babel/cli":"7.13.10","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.0.1","@commitlint/config-conventional":"12.0.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body() body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                 | Remarks                                            |\n| ------------------ | ------------------------------------------- | -------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings. | Allows `'true'` and `'false'` as valid values.     |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.    | Allows valid `ISO 8601` formatted date strings.    |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.  | Uses `parseFloat` under the hood.                  |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.    | Allows strings that are present in the given enum. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"3f475afebf27aadffc46973ef567fbd9b72f4780","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.5","_nodeVersion":"12.21.0","_npmVersion":"7.7.6","dist":{"integrity":"sha512-h1CGvwz+OV92dBT5HgKkXjWNN3UNU9g+Ckuv+w0NQA2gO/2eKw3DrNORP0kRbADMIQK6DR3wOEz/Bx/djYBXAg==","shasum":"cb403beae6b52dda1f4062ff7965bea781a028de","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.5.tgz","fileCount":80,"unpackedSize":165619,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZLseCRA9TVsSAnZWagAAqIUP/RNWhQkY4pAwyH50CsAK\nrIZ6TepbN1M0uZvPttO7e64y5mL5BtC/TiDoY2kR2S4X3z2w3JwRg7rcnAoY\n2HeJirlR9Eurrgxby4C7oYBBDE4IHrwpgYjqhUNw36Wjo7q2c3eEeaCe2hrd\n4xHXGPR9DXqRnL5NQ0HjZD7GEIG3HKxbOyCTInDBL1l8LYAuUFL/wrrqudSl\nlWkKIwDtKDOafbkJM9PF7BZre/JspWC1AFOF6kyMYC37BPZtjK7tIXrhVa4w\niPwmOPJaZLr4Ha+r14PiZujC3dI47GudHf6kwZ6W3u14rDBTGyOFtIGIhPd8\nSE6payPw2WjJJxxM3LD76mK3m9VFPf/q/m5SylXaRrvgmaIfQxUlPVVDNImj\nIxcxwVqpnbWkHKhLcInwojnD1B7rODVEELTKX9ayKnB/8S24+YquQY7HOY7G\nRmdZm/PCVD/D3bzYEyGXd6rSpvNTcr/4mvwhbCBKHstzLlG2wfpX4tIo9sW3\n9a58xsm6ifi092xO4ScKuI6Igx5YoVfswTERI1U8iSIjyDUYWehQ/MOQEZle\n5F7Mq885AcQkaI5oR8zca1IMVskDs/Z8cLL++6apIVtTP2NWN6lGKsH6QNXu\nrBdQjDaKwQwXaiL4ktmwRs+8tUshBS2uJp2+IG/b0v7xvYgpGTCJUsiQULS3\nsLuW\r\n=fElr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZH5C32uToaI+y7IxUIiijTYLoEZjXQx2WPRKC5kbMrAIhAK8IOxgefFOFePmb3FZ+60t4WszM6eI7NaBLiQwME6iG"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.5_1617214237809_0.4668356126169473"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.3.1":{"name":"@storyofams/next-api-decorators","version":"1.3.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"npm run build"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.0.5"},"devDependencies":{"@babel/cli":"7.12.10","@babel/core":"7.12.10","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.12.5","@commitlint/cli":"11.0.0","@commitlint/config-conventional":"11.0.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.13.0","@typescript-eslint/parser":"4.13.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.3.2","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#6.3.1","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"4.3.8","jest":"26.6.3","lint-staged":"10.5.3","next":"10.0.5","prettier":"2.2.1","semantic-release":"17.3.3","supertest":"^6.1.3","ts-jest":"26.4.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"husky":{"hooks":{"pre-commit":"lint-staged","commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"399fe4342c9d8c587b4737fc228e368820c5c07d","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.3.1","_nodeVersion":"12.21.0","_npmVersion":"6.14.11","dist":{"integrity":"sha512-+WCHZrcZfRmhghMkdqvlvPwtShEYq4wxL0bBKGT70n25BwWiJHk8hhSTMPeeVerSk37ZLCHolrXkQuVMoNrMYQ==","shasum":"0f08ff04d8143bb3b3a657f353e97fd2fee45ff0","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.3.1.tgz","fileCount":64,"unpackedSize":139746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaXNjCRA9TVsSAnZWagAANOEQAIlqzF7PZGZq37iLnr6W\nj73+f5cKP5gPXJe+Ia6R4cnxbiYBz1UAN+y3KJG2YiuKUFi/cyYIe0d5W8WO\nR7nPIdylNvluUGsd6g4Avn9abqjwihMmk1k8shx9+lCW/KHoiwkl7p93eBfc\nCUPcwnPPHfAAVeqG3AvbZTanTdKMa/qh5HUbnsBHU6W91FNafsRtgGvWmMt3\ntbO0yuXj2iCBK9x/dUSOEdYsGroMFwM2YTScbAx/v9UA9tdp9qBgDUEZ2bPJ\na/W0GhfulodaWkaIPMfzDMJ/BnuzI5rBDTCqhmgD83YuY2VI3D6w/CBZYMg7\nxmAOR0n96lz5OvDuU/pOQUNdgsclKnuoiAna/7pyg9nJAQlxrdGo4Yes8i8M\nlGimvzAY/4TKN8rKxLgu7VW3davxivj3FjvrfSXosZYaiHmXzejwjac/hVa4\nRgr95sh8D78LrkxriImgtwkSg4NcoP4IvtXyhhkR8syTraa+BRKm2rfxFkhn\nimXzzk/XE2x8yiLA/Ij+jk4SsP+2oveFb9CM3EKewW3N7RGcAFnTOPMik26h\ntz7BINfywGj1NEDHP76+IRVA5EGqr1CSI2ioxbYlsQr2om3k8dscKkvTUSw8\nie0PT7J1rChFWk855K9OQXmO7eLGJ6ma6BeZbnoCKbT4QRTpAb6Wm3nBKbPf\n0iT9\r\n=flVH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsCvG3BYz2Pqz19o233DmcmqQI4NgTZ+uXpcbb6J7UZAIgfxerHHDkc98g5SjZuedsEGYi6xf9jNmpy68sV2NeAZU="}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.3.1_1617523555021_0.5526342804813023"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.6":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest --coverage","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Basic example\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Get, Query, NotFoundException } from '@storyofams/next-api-decorators';\n\nclass User {\n  // GET /api/user\n  @Get()\n  public async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that they throw when the value is invalid.\n\n|                    | Description                                       | Remarks                                                                           |\n| ------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings.       | Allows `'true'` and `'false'` as valid values.                                    |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.          | Allows valid `ISO 8601` formatted date strings.                                   |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.        | Uses `parseFloat` under the hood.                                                 |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.          | Allows strings that are present in the given enum.                                |\n| `ValidationPipe`   | Validates the request body via `class-validator`. | Works only when `class-validator` and `class-transformer` packages are installed. |\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"00825566ed9ca193f159aedd1c549de32a551b19","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.6","_nodeVersion":"12.21.0","_npmVersion":"7.7.6","dist":{"integrity":"sha512-MpjWUvqzsxafK34EVUyS+MrZp04MLaKop9nX+nH+wQgfK1Fuk8KC8qPCWMquOy3Su0zt26kqPya0zXv7mpwhGw==","shasum":"abd14d37dfdfc5218c3167dde87fb4f9e0b48824","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.6.tgz","fileCount":80,"unpackedSize":166030,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgacpCCRA9TVsSAnZWagAA70AP/3nBw4glMxTEVMclQZwh\n0y/I/b804ez5roquakJBZJznsTUr0OfMhpJ0AKLuKaPfByu0YPMB6cDsZ1MQ\nIeiraeW9FLbJkRH/1ZridQpmUdoByT0SeVNTCBRplDfd/2ldusr3MzuqBz00\nwtMb2CfnsJ01OfEaGcbaFFhmlm+v3AzncoPZkBYOdbW/LC6SB+Hv/5S1JRb/\nM+rm4YHBamgwAwGi2T27y8JopfjwTh/g599UVOT4J87APcSubvSnKfIjxqHp\nw12y+G2WDEbUwLy56O9KP+uLvHXFFU6QWRdQMqmXf2/Mkphkyh9sMcl01Zo1\nmSAFMgKMtU6V3IL/kIzc6C07oznjS7L5xQIxAbAcv+QVHzUzCrJPSME23GaM\n2EOuwZYkUWW0arMmNqNntIwOOK5QrLlyKAhtgQQRrfiTB/30yyTLxCbPNvWb\n59CnSKox67Xv2AFhjGXYN+DF/xr2rEjdqaohRCOcjA0WGXsX9Cg/a+e5HHm8\n5Fr3p4EKBvK7+Bx6Z5EGdkAWBcAHaXaXU5c2YFGp/rDnEgxa1j0h1g6NAoxq\nrX1yh63VA52yXEL+XSJMa+riJ/sDtJnnSwhvhs2d09svzQ4EM+CaY+sELZyK\nVZYfSs8r6cSnQ8ixPHUywiaOn60juqCi91z/WuQ9fvAeyRvnTfxMGRUsJ+v6\n+eQC\r\n=tyQP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaj++trZ5XFGYznb05Aro/TvyrGHQ71dE7X+I7djpQCwIhAKunG2mXKzbEBtbks+uZqbWv2SXg8Y1lNmorkEhzzbfQ"}]},"_npmUser":{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},"directories":{},"maintainers":[{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.6_1617545794324_0.28932475540500713"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.7":{"name":"@storyofams/next-api-decorators","version":"1.4.0-beta.7","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                    | Description                                       | Remarks                                                                           |\n| ------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings.       | Allows `'true'` and `'false'` as valid values.                                    |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.          | Allows valid `ISO 8601` formatted date strings.                                   |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.        | Uses `parseFloat` under the hood.                                                 |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.          | Allows strings that are present in the given enum.                                |\n| `ValidationPipe`   | Validates the request body via `class-validator`. | Works only when `class-validator` and `class-transformer` packages are installed. |\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"4733b144d89b876637381cb92ad1dbb1d708676a","description":"<div align=\"center\">   <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">     <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">   </a>   <h1 align=\"center\"","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.7","_nodeVersion":"12.22.0","_npmVersion":"7.7.6","dist":{"integrity":"sha512-yHdp4p3KpVUWxDYlE7tJYnawTRqtr9k8wg89ZyWiVHh+kMVupCclPn2CHaCe6Ttsy9Cf+PViBB4feS528Q5zmw==","shasum":"d9adbb983bc62413678029b88fb5b31ca54e6078","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.7.tgz","fileCount":81,"unpackedSize":168572,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcYMyCRA9TVsSAnZWagAA1xcP+gL4nYftSxcKYcPSi5HL\n610RGaRrpXUuAIggbQ5jDkhyc6msStWbxRgSZ+iMnoFXuPoBW9DIZvM5Y+y8\nzUASZFWGtAG7KFhBldu/prZNX+/pCqVbXAsjAVGpU54sRkg7XMA8f+2d5mqL\nrDXbscps7lfbPSc5Ij/kUAz72S3yGtBP/4VKhXq7DWiPum/1CkBOiTsGLFcG\n4FccsaK60xuCWUtXcEtB5NI5xc6Gm8T0N/C3je7h7jvslMfAECckrECtDtoZ\nk1CcbIiucKNy8SUic5TZKMx+qOFMHYajQYZQl61ibX6Wyof18AZhIn3YWlBM\n16JAcS9Aw700kS/dyoepKSpY74Z3lGfaaXxmeo4AbjTxs4bl9cexgJDqeDgn\nFYwYWZeRPXGM/Kxl0G1SunAsQRh8/svCnZp+kU+QzItUE5pA62h4gOlOvjqX\n7IJHqR93Wch/OnoH8KVOjnUOcU+d4h76PY2ThfBqfPGonSinSz6QGINNFNbj\ngj4Q/alGOCmWVavXstredRlS7LFtN6dYLz8sHxrt5BZcgNalvazm9NyDB14D\nUEm46Cw/4B9zBRQ3Sur2o5XpY/08dpArx9up6IiGMQ6+vz227DPlDexmIIHg\nF5SkxRPOBUkLeK52Xse8aj0Dpbkmrk9yEGHWbFc6SM96cnI3cnH/I9S0nnsj\nH4T9\r\n=llVg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCJswG+Hodti3nZmN6xaC74UgEiuuf0Gbsyb/z662cBgIhAJCnlftFBBF5J73mpGLrWfRSHQuvtVAOdWJxv4PWolqv"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.7_1618051889821_0.06382056223134414"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.8":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.8","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                    | Description                                       | Remarks                                                                           |\n| ------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings.       | Allows `'true'` and `'false'` as valid values.                                    |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.          | Allows valid `ISO 8601` formatted date strings.                                   |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.        | Uses `parseFloat` under the hood.                                                 |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.          | Allows strings that are present in the given enum.                                |\n| `ValidationPipe`   | Validates the request body via `class-validator`. | Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe` | Assigns a default value to the parameter when its value is `null` or `undefined`. | Bare function usage has no effect. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`. |\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"fe979669d99bc8307ee72fbdf8580c245eeac129","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.8","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-gv4v88T32g1/D9UMp/HOwuBulBlByfpr1HVGyxexE6qQbgwMsSRd24ma6KQGGo5unxXfCiv2SrrTzD5jWw+WWg==","shasum":"89ca0280551b382d4857c1f59ab1ba5dcaba2863","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.8.tgz","fileCount":81,"unpackedSize":169624,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcdIRCRA9TVsSAnZWagAAJx8P/iXPTZcd3vdZiFlamRtE\nL9//GZtGTGkYVHKHCRZK1S0nWPLiEPA92XAHt+xDCnjGc3kG1jUTymUcMan0\nk5GQs6VnNakmp91DH/qFhRsOFmgux+8RipCK1QV2YogImJ9YyL7D+o8N7yYA\nnyjiGep+fwDIvTZBa+UD5cZ4MhJP1buy+YIjbE4r4fy0zF4Ziw51tplkwiDJ\nbBV4yrM/Ud3TvLSY0abhY8rMypiKuO+YGsJq4f0xjc5/ekJkWfEyr5SDD5iW\na8UZn4PgMY6oZ4E005qHGC3n0xwHy4O4Dm5vtMw9GGuBXzq47xH6yK0zAKjY\nYk8Ar1h4ytJKm2MCCJyz1reYHsvwuhuexWiVod9nQ9umUy1NP74JzJi5p5Wj\nQJTx9ylBX/rY1p91XCBykS/nfUjQ9Q0HsbDp+fyHuFpnYmEZwlZHjqil9fGu\namtZkriuRJSjQArm7g6aSSku0PtXmKjLuxZfitzcX8EsTAs3MgfzTBAUiEJK\n65YIMfk8Hg/wJb42vUJekRkVpzacBFYcLdg8AvsbWZKW3FwZcpbFUyDDqjSx\nn9j9ibBocGeSJqldLFcj2NjOOvuMcPcks2jUnGzPvD939FEAaYjqNR9CBxf3\nzPXnw24RhukNbsO0zfXofOP2D94Nxy0SLY4OnWvmS9ynOkKNY7cZmT0BXg/7\nOdQp\r\n=8HWB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4qxsogkkxrHsI6YHb0VwardY9IMISZR2qvWCZXxWTKwIgXvaeKn1Y4cOjaBXyWXmM3Yy9V+l5rAKaJucpVfbkoZc="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.8_1618072081115_0.679752474962749"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build","_postinstall":"husky install","postpublish":"pinst --enable"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"b7b06f6bac80f49fd38ea6ff7cd724aa28342fb2","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-hjDKMMjwzmghq5w9uhEWc+dsSNOzjwjnqIm6WsnrnsiyE14ohMx+Y2rE3M8w66OsW/njgSuaJBlM563BwC7tqQ==","shasum":"32418b91d054afdebbd4f0370091cabb93a45b5c","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0.tgz","fileCount":81,"unpackedSize":170580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcdWhCRA9TVsSAnZWagAAxgUP/RgnfEE8qq/nYy2DDvnL\nHNVhZRCgNE2CQbI90VM+e+XrAJvLKGbcdur5nzNRPZTjE2iu/DHCGTIJTlFy\nviIWJVQdhmsWpRh/cc3e5DG/7seddBPshf3pml/paFpF42VRRwvaUmKdWNEo\n4eivHzmPPhAWARf2XiMkxQjkx36gJ8nhiXbmiRSihWh9uEb+0NHfb+qpzIKC\ns1b85nPrJupiZY9NhwzOD84m9txdVbQjqJKcArw2tKEONDMvT2I9w5jHrRiz\ncbn32eXUsMkD81M6l67AvMXe+PzeWPWHKpC9jYcFFOhvkbOh7LJwL4FOmoFH\nw1Rkfa9IB//rPB7BXGM5+77TjXNojFrnnzDZtooPDZvgfFzkHLNOTDRtlCLQ\nsHydjZTkzwEdbEFwxbrCY0YCr2VJlmz5SSSVM+Oic+1x33cPNCTYnIP5jRTq\nKjnHc4R0r6+SOuWW0lUp89ZA6BpsQI4YZv+rdioRoi8zqB+6orZEslb9L50O\nCKERBc/TDGBPds3UNw9a3MJ7jvSVeXf7KIEpiPyW1Il7m6/LBydEf8jlKUTg\nVpNLYBLYaqb42V3BYwhsLYgEdMzNvI3y8DS+3tgJwhRXei/6XAFapI09CuBa\nNS390I4fCUv1PLxW00Ef51wIkDyuSkV6zrUn9UNNRyt/XOGXMikSdM+uE4rA\n69BV\r\n=fpsq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICikLoKy/gct5HzcjzIjc+PIriiCjlhbOUKJenTLxBFwAiEAufM/GbaFpPPd9OCf0aeFm9+RD0IHPS8RYJT1jIX1Y4o="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0_1618072993355_0.5755346518113291"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.9":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.9","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [[ ! -f dist/createHandler.js ]] || [[ ! -f dist/createHandler.d.ts ]]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...api]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                                  | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                                 | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                                  | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                               | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query` decorator like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                    | Description                                       | Remarks                                                                           |\n| ------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------- |\n| `ParseBooleanPipe` | Validates and transforms `Boolean` strings.       | Allows `'true'` and `'false'` as valid values.                                    |\n| `ParseDatePipe`    | Validates and transforms `Date` strings.          | Allows valid `ISO 8601` formatted date strings.                                   |\n| `ParseNumberPipe`  | Validates and transforms `Number` strings.        | Uses `parseFloat` under the hood.                                                 |\n| `ValidateEnumPipe` | Validates string based on `Enum` values.          | Allows strings that are present in the given enum.                                |\n| `ValidationPipe`   | Validates the request body via `class-validator`. | Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe` | Assigns a default value to the parameter when its value is `null` or `undefined`. | Bare function usage has no effect. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`. |\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"cdb217e5bd00476263fe133b776018bf12b0c2f7","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.9","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-ftKLzzp5rdhgTCupOS/JXtYr+o+bkZEWesgNkjXfVVJitbOEC6yIdakmhBgWuSxcDIvaVnreESoxR05dPKc0Hw==","shasum":"7cc157d24181850e15116c14df5c97ac1b6892b8","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.9.tgz","fileCount":80,"unpackedSize":169405,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdDb+CRA9TVsSAnZWagAAd/YP/jgizsvAoorjJbhdMWTT\nG1qkMmbwoL1/3vLkddto53NcKQ3+MWngcrN9DLJN2Wby/mHb325U7ZL5gUqq\n6B14P0ohFHMxcltkJx1tCjRlwDRsFEdlLpAobwtWsdtJ1RSakWo5ULr9hiDG\nnImzH9pzPoaMvt5SlnK3gYM3+fAFg0xwpKLfCc9QJTcuEFy412HxJA7tPVrW\nTd/6zGuih89535oXze761zqftcWURwFo65LdLbPWg0ykm02UcHXuHtVjTyii\nMsD/cXWe0KIsv2Y7EfrmxfjWXfZmuMV+usApI0jVqbuD7klxWVFqveVk7vXV\nXKJG7hTloVbLQDiT/YFhBmbBzamZFZpdfwRPzwfJ6UUBzuNF96gO0JTD2oF5\nYMIPxJ6zLe0NS7Our7JpolQ5QHNtCWEAwBcysgkJW+MgOob/Jv+8C+ibUrvW\nydN569AKxALseRcVucIf+16ggKJOAjUAoUK9hAFl7yMQF7oqWZ1CFMNM3Hgk\n8PXocx3cMF/XaKS7srP3mK4tkb9HKBPOKiMVhLhxGRRPddiSf9KkleTWmeyI\n9epFnj600C2zIJ3+Ltb896zphTWaZYP7ZfxNG+CNlRnuiiRr9aROzT8Krrt2\nkD8UiHUqAcQCzkLr0vJGX5Dxb86rNCVLar+zeUnhe2+bV8ycX//6Fnz0LWaa\n21S/\r\n=C2a2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYzcw0tqS3x5UjnKZRZHCxQHSWDMn7o/fWHa3cln2/yAIgd75OzSUgPcGnfzlZVITTLxT4utigTHvZrXys9hdNv3Y="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.9_1618228989514_0.6088612942702789"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.10":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.10","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"#!/bin/bash\nif [[ ! -f dist/createHandler.js ]] || [[ ! -f dist/createHandler.d.ts ]]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"265085d5e26a13c0f42c866495e2c4f0e68f4159","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.10","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-57eEvdEa9ffsfAv1sKG/rhuLpDv9s92p5NnTKgOengqmmwrxUr8NDOLelmrPEpHYBgGA4rBTUzoO2IOhlZm3/A==","shasum":"603247c630fe19b63ff19728247c16aa88383c57","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.10.tgz","fileCount":80,"unpackedSize":169335,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdFYBCRA9TVsSAnZWagAADS4QAKLbRfqej/V5Xor/qFJc\n4L3xEMi6NFc0yvMcjRUqeuBmQGuTr2TmYby33i3rLbx+aJ0U/ZIL5xH/y6Ej\n9p8/FijTSWrUk5xDt+mqHg6+bpa6jT9xkw/c0wKQYRZLAKrTVTIG3+x6FooR\n0U3CQOnw/GhNcnxM4ieDB6Csn0ZVxMZXurqGraoWRCffMGF7uDvoJ/K+sCMY\n6NHyaTYKYHfd0jUGcrSj5I3b+4VTtmUDnqndgrTc1fRYvnyruppjf3CEIpr9\nVAxU+03f7hAmW8X1tGTKqP7s8CzNU3wI7AEZvZOQwbdH9DCvmzImVyUl+4LQ\nthLWj1Qvr9di3PaNk1dxACCMPm21LSiPrBVMxBU72RfK8rshaCM7j5bx02i8\nOaZAprIObeAK/aTEgYE8i/nUG1NPVFFMmg+44hazXzZXOi6aGdB2IYZPsN9W\nJHcFn6Y0XdP8QIjRLEtNESrt8w/vaxfIpnrIBLWZVmLyQud3lLQv5DLBRgGW\ngNGejrIiy0Sv/V+q/40qIkc8kAUHUIDnRjcdQUAwuxlgGJmx73B14sNtCRO9\nz4iOwoU5F01ePC++eIrKm1/XaMmFn88YqKceQlrFmus2gxP8X2m13WwNzHFc\nZMUEW6W5HX9wC+KLomC+I2evq2z3TKkyT6rqQHpgBkDLJk6E++eF68Iy2L42\n/q2Q\r\n=aMx5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4I/qoooRSJBZTcU8YYbgIt5j74iyyJ71SVQFfETusQgIhAJlr1gtB2ddgTYAUxNDhLYGPcfNFB8MwZDePT9O4ZRKA"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.10_1618236929223_0.9788014329210093"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.11":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.11","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"45e8be62af1ed4dc18b48118944648ca3c361563","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.11","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-vPef/m8HUPrDFxb+e8Njv0bHR+edHRrT1RCXHvtMkQOhC041YQ1+LtKMOi/SWF60aWxOQ+xpuwAIqfG95vtHOA==","shasum":"eddb7368db433e3f47c3344e9f0bee75c82ed3ab","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.11.tgz","fileCount":80,"unpackedSize":169491,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdFc7CRA9TVsSAnZWagAADRQQAJGcHMsX84PYarsI7go2\nHpZLb8pySBWBNjqzTjguxsqyh/WPELV/YWEdnLPe2RmJ6t9EcoccIIZi1tEl\nQ6oz2n1kcVOR3nfVPcuE3FFvt6lYP41vI2cx2zRXdqubEmJGJLHm2rOM5rUW\nb21YpVIPXnZYzHWK7kgt1CL2spXX8YzQKXz8LRoaFYSI2CiT/0yzil8G0yoY\ntlw/O0YmOHQnBMKaxJ9bNCGdi05KShG/K6t8tAejJFFAR5ijD8/3omUI7x+I\nDcpyYF3pq6kBXR3VXNFyFcAFJbbpvmhjALR4nwfb4qtTaNBjPbkakc2FYlfJ\nAGrzP9gYI9q3EEU9rX7iVaZFIXTfmETyBTKZVnCbG5c5CiiaPb3onkm/Va2d\ns3n4TPDRtOyFFf++Lg5Exs/hDkfXVr3wzP6M/xAinSNcTPvTC7NdNbFLphcQ\nWePpRnYJrG3+igycMEBxbsquiadLZrZuDNZgpOds7BhrDBCYflyVCWF2VH/x\n2QpnV0sOPuNtTEGPgO4hB/7u+Wc9WlJlfMHYkc9aQqgFMSK5FP7MKsXkqKJU\ns+WXl7g9Ml3sbmacRuNMpgfgt2cVmbqCH8/o34SmbgezRfCc0bicMXXKhIgp\nRzsQ1AvrquHyfajPlcyyK/MybDsG7pAhsmjgtZmWPYJtlb6nRxJF167FZmVx\nCfNb\r\n=R7It\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHQUcG0W7gIPyBpi2uCX3LtVIHSqMBPZoPMf0kNMs9kTAiEA8YBzcc3OiI/6aHT70+zp/W42LlL4QjtlW0MgdqbGLVg="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.11_1618237243465_0.8115908995053185"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.12":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.12","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request bodies and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"5a82eeed97bfd5bef83f0d34d69498647fee98a4","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.12","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-/TpUfv2NfY7rLh925WUbaQySnx8OOFc+LvLyB3y+xvoy7bR4IUfR0OgR3iMTAqu4/3EmS78jVEJzpyPFEsATJg==","shasum":"686c5ef3224c8172dba931546036dee6c4154ba2","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.12.tgz","fileCount":82,"unpackedSize":170171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIRUCRA9TVsSAnZWagAA2GYP/37I9ILn2OzO7+JWDphl\nUDP1CFUZZaZbnvWKSFZil5YYpdd69NTuxsa91MGILUnBwleTbtIC3iWLYVAg\nh11GAyvM3+IAQYD9UDgnIUNG/SQf19/R4C6vDVBpq6B4waY4eLVrmThHwCu5\nTilOyPhHOGJYtr2W7+eB/wl00k3WNwor6PFxJbEIgQ9b76k7j3Bwu2W4ZmoB\nqsqV83XLmmOv86JzgXMm+XQ5f2yvkPl3nUpJk96wCt23kM6bwPSewokSpvBm\nk9f3znSwNe4Jp+k3iUuseQaMvC1UnGL67SCZA1jrBqEGBSDJKEOHuZN8eUzK\nMLSCRphjQh/778EatdbOFMnUHTykcXieA3aB/IteSUvjH49WvYZFReBGcQOr\n4QV8HKYh2k443y0i41rjkqfWd79XRn1+3TFYq9wjsOQla03Q+RQtHub+O4/O\n4+0UZB/AQfTBnzXbzftm4wM7fOS6Zuo5DUTEq47hlev9oRzU9sGmVMPuB6a/\n07+z1jFPoE3+KJCXb6FvAMViuIDZ4Wuk8Qlickr4IKCu0ofBppxgkTcbJ2BH\nz1andYMnOALWYVb1N7SY53bI6/PL1KzsaLF/hWWNJkNwdK9/JPiZ6LDBfFag\nuXOGNAn6INZ6nCWgj9SFLK0+04wR8biczW03j658EXGxXu0qzQJ4D+q2beX1\nCDuZ\r\n=nRJi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCw4m4GwP8TmxH0rFIRRg5cjKh8A61+kO5bdf7JpW2RpAIhAKxDfGb5aFgUAxYKxtzAmDlZyyqq4vymizzmQbxm3XU/"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.12_1618248788308_0.5433594696291983"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.0-beta.13":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.0-beta.13","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"ff73754735df42eb7b2ce830777ff552623259ca","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.0-beta.13","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-2LlV+UqxexL3j2L4H6Dj2jvX1y8PyblVsOfx1pAHYmLL7+2CRqsShYwkSC57MOiBPBWa1yTn1uWrpYcVG5vtsg==","shasum":"6e555ce6ace67f4b73d7b84632d99495677feede","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.0-beta.13.tgz","fileCount":82,"unpackedSize":182184,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIZ3CRA9TVsSAnZWagAAB8UP/jbUou7DXsMByClGpG1O\nQOTg7n0vBblbiur6ljWVCzh1lQgbb25zpIBwjq2kh0W5u0XijsgEHJf+qcgQ\njFlGGCWJtCwmgpflGQObbJGR6RmcGxHjTpAsgekKzWaE37s54LiQxkVOamWb\nTV8cvrYOwaw/Y0ljQDf+fTV3lFX2g/946PZrStzO7fg1/+stRhm1ToLl1DFe\nOMzkKoK9S8t52cENESkb2uv+vwumhnra608QtA3PGn4m6+hNPEP66z9/udxq\nAKxoKFtLVk2OVxR2rGZPkDQOZgy5drxfZn1CCbyagRbvhbf4J9cV8bcwK5WJ\n0ZlUGUFzNertn6gtmNeq9/uSsLkj1hlAYKkVGhlMfZ3Ev9+Ibivm5ENCtmRQ\nh/jRA8E8pzHH0fUZBb8PSOKZByBwidHMBF6dHSavosjGFiIiMsLE8GLnvYp8\nQ4Dijs2ePgN6nhvQilxkfy6iSCagnkliMnSfdjp5oq9q1yUO0b4J4vtq2S+C\ngddGcL2QdBps5vMp4/xgIqQy3ywMpSf9fLaVFrHu8yOm0YFYi/whLZn2ZY5L\nZy9QGyOI5goC5pwZl9jXpcOWfLC8qVeU3kYCwAisq6WhYpmBUWXKHIhqpQ2y\naRBQOjfF7CyOIrQ8J5M3xPbfUW4d4gzazMmepVf0b3ktU7YHho69VEwOTJO4\nJogJ\r\n=389i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQZVkCIJ4auPcoiCZRH84xKmp46zh81lnGrlGjj8QQnAIgFxadw7OSxu3PJCbiQu5hjibsYWCDmuTzOsPxh5HZQkY="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.0-beta.13_1618249334519_0.4525456491776372"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"e8172a99afa6856bb6354fa6dc4b730de7fcfef1","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.1","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-uYQm06f7u6n0y08/Qw455h8AItRzt37cqs9d72ceBIT1Ics3LFdjvJYecZIddYGzWudoIlP946PnSzIe2Ldv8Q==","shasum":"f5b9d43a9c3cb87908af4667f8f5c518343f7581","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.1.tgz","fileCount":82,"unpackedSize":182491,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIcJCRA9TVsSAnZWagAAme4P/37p8ViBnXG9J3+30Pv+\n6hQbsKZDgCht5osqsQa2v/71c2BME9N/uWPxRB5upfcBrPJGH8z0oXLLZxV3\nb/s03oAC5RBXnRFIbzZ4vtHBc4xMkaIooApzgqKYxGeI+73C9lqVlbpzosI0\nBC2Lbd+rRSonEDVunxLt4C4iJ86BOiyyFjLf50qc9U/PH4ZbDGx3KQMMyqJd\nsGGff6An+SF1gRXAj4yeuCVTGn8Eprr6d7UI/MaDLdGO+eYocMcRG50zdhk0\nH4xzwlv+YahtNJQ+Ux3kZW7u+LrbOmzV+saM2zjdffmbp3R+mDnM7Tb4Yzgp\nY0zO7GBvcOlJsmPpmW7F9sc2F8OL3eiTrCrMIYbgJn0Jg6o0S3SLlmgS9jNA\n3CInqJgKgidhlPPWgE1mBV1r/YsYlr/o5XuVikN3jgE3X7fuuuUrV8QCJQB6\nw1YdPhERvZpZC5ESJqfRctP3uhRejD7E+xhNj47IidomDvdEwtaCEXFsOCW7\nGyF4y8aFux4ea0/31RFz9/k1JzUj24m1uajzB0h3X2zhIqT6p/QwabOR/1R4\n5PgdYef9HepJcp2fc4JJ3yCbWE2LYqSLBu5LCPMR27fGZWBH8f+UaVLnpXj/\ndggrVpyrgd1uMv5QmRWA/fnnRYjJVAz7ZDrk7ov3rSvYoepx/MJnYnBShGwO\n/k3I\r\n=DJNR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoQtmJJwuGCvIX8BKN2J1D6sYJSraQe38c4iDfTzvh0wIhAPySDDbGPNgWgvKziyiPr3JbsvBwa/PU9a+Kw/zERLpk"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.1_1618249481117_0.6054155071737295"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.12.13","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.12.13","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"e68c91601a02d73f0ed5b633fc26bc109d9c68d8","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-an99ClfYC9qJdJlkh4Pcw9A3iwKTRxRvbqWVG9jfVXoCXUlsZaBsrvdz/PksqxPhzInyWcxhc1jo0YhKHdyIHg==","shasum":"f09aa6c415c1c93a2852a06226c8fb6ee8b0fbc3","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1.tgz","fileCount":82,"unpackedSize":182470,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIeYCRA9TVsSAnZWagAA7OwP/ArNvvt6R+sl3O+fCvwk\nHCXJoGvwGr3docnUhuYpisJkB+Ui2VE196MiKKl2I56m7sf5op6867mfSqLq\nIlhIBM13DFRhegJaX7uA/WZ+Lunwh8NrCbPrgCXa34S6UKbSFsEqVHK1LCG3\nF2HmReylyZcm9Zd/92Hp9OmKcmXjU5qgmrmXV9Njag40+P4aEYVbwGLObZHs\n5BJO1oTtTQvomJOKnSoe2kfM+pM7QiJQ+g91bqObGVQBcsftugwOQsadAYCe\n0Gnh+J8zV7MOiIZhz47L5oiH9T1vBlxNASf6nqnUqXjjoEtYqN3jZfAY7yEm\n+IOYJScwTCVMjikwNFTcd6a20ghZgP8d9e3gX6KuYx5pJX//UZ4H5uIHDKQH\nZZUgNvxsaG7WZoTCxEgL0eA/p0SbG0LJp2FNUW6SRDkpSVJpDjYGophOzdTl\njywkiZGV3FdvDoBtqmdggXDAnbPxfxPdOeVDM0suJUuXS/frpm7MSTA3mGvp\ny1uBu9LwCRY001/NTd8bwRVeC+bfTjrd1hAV0JYdwJ+YWgOYAm3FLDvkF7vD\niF9sBhS7cETzEDi0SBooNNoFm4ARKouHPv3ahgiBlcc4vULcsm99cg0uDxIU\ndFp+E652rZGq/MmGnQJhIh4gZLxT/8snxURwiCx854PpcAN9d6M06ByPoHr7\nVFCP\r\n=A0pO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH2/Oc5cy3HYhHmv7gVtLPRpHtgAWlGR8nHs+kF3S5rQAiAMmg1wHveqzui05Uhw6wBbJy0iaQ0TihQ3r485wz1xCQ=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1_1618249623571_0.32049316491949"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.14","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"ed96fdc5cb6b0338cb1a1f4e563b969f1825a628","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.2","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-nzcQN5eNkIpmFZBRBhDoVYCRvimIr9n1RgLUUXsgh03NCyrm4msG2kEVOd+xgZYxQICC6fVPL+kB2Pr1Y7fBeA==","shasum":"3c436c32cb0827246d1c97acf7b87a87d301207e","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.2.tgz","fileCount":82,"unpackedSize":182181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIpOCRA9TVsSAnZWagAAY+wP/3EzId86SAsN7YJ7Hr87\niCWgrNFcR52sS50WZQ255XpKZGgmzzjj/uJvRHcT82TybV2ef3nrxFDiKe1G\nMgoE8vzXL6L/WgJOs6IuRr9Dha8q1Ag1TSK7TOaELPoVX+M0Ivp0PUiDdbkx\ngLIQ5iC9/kN9uOQ55YDig0++hhEDCN6RVzpqac9SF4SMWGwlwpsXMpvHY1ek\nTHFhFgmpgW/MCus9ngohIRIwdUwSBXoQKJI44rlCRDW0NZGwlzz1EvcvCWMx\nJo6/ZNR2OrPGFDWBy0yI0uWknjG/elnEfvb9vTl1jFQeTwmy1wDlb1QQmGfH\ngZJxdeH0R5EjtUvJDpVyfG+dpqAEKVqcepdEDkbBAVnE+J5d2Jf8XwMb4DFm\nL7Q9G//REMvPR7nY0GpgcVB9wMEOkjrZ/hKlOohhdEy0EyDVDDBkkTjfGZpV\n1QEYmerf/OGRSnQ+y90dfB+Fbi560AXVSx3jvTEUw7cC52bbpbZX+jJuGDg4\n+jNT5Szr5loejjJl2ExWudwVl0qRsKaFlNynsRmxWcgbIxqlj263osL1ayze\nhlP5Pu1jGnzAvPGeWhH2WnxKJ6ROL5TNS9Aex+XyisEPNcqNzA0oY5+X2sT5\nyNlmsbs0CSOaPPmyLS/0oEq5gTdyYWgQqYr2tiD/ie6udYnDhpvzcwRowcIY\nuZyF\r\n=fld7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFTZg4Hf/CpHVZJv1xHSBOFoPXf7ESZ9sbX+ALeDzZ3sAiBpYLWX1LHJiHwfjsy3ZO0PuShDZH3MEatB+P3Mks9GlQ=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.2_1618250318234_0.23565525967095602"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.20.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"d95fa8d2a9c1fea4d5e7366fefb869a801bcf34e","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.3","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-luPWfGBxPugrX08l+f5nnDR8C6dkOhUduZQDzcoJxneK7WafXSK5N8VB8REwrR+kEMA6FwJ7FO6IiB7A8G17Wg==","shasum":"0c35410f4694652c6f04f306e5fc7dd7d7ca70e7","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.3.tgz","fileCount":82,"unpackedSize":182181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdIxSCRA9TVsSAnZWagAA9iIP/A0GnClK7CJ7R6IjXkjj\n222wY0k93aUU9OmhymBA96inXG8tamjB1ZKvnNKm77yODrmwkp85JsNbqdKG\nAqx48bly1pB+f9wH1WLVw3k6xB3fCuFodHMJCUBMpLnFXwz4jHCSJNKWll/f\niIUuOp5vdhgz0pN5syGEp9ufMUPirD4CNlR96x48yRx0o1+lNVx0/D+F3r6m\nFUOyMwJhCHVx5rd8+OUzn75AhRJW4FveEQvZ+Z8WnOvv1/Vr3pDDTDXfP3xV\ngrbevHzIKFIvUYfwme8Xsv+A9tpOgu1u5Z4wc2WapZqClLnllmvdYnaCJ0qA\nFmb1gUgkFG+cuICo1Aj/QJFVVY+Wew8+unXyyFXkpuxwUVw+SerAQ+wAQurh\nprrpaGg13y55od7UZJFwsX0C3XaV2uwFHaJ+04XS/v8lPOFUeKfxm699Dn8J\n/79faRj+sgsbKwon3pw1I3lB4FSDgY9vCmx+bVzSAQ5L8iVcyp8o+vsb8/0F\nNdzxqOkVNtwzNhkmIP0hhBaqHHotbirMPuVyRLNLerDQTlZQ/bO0zC5urHxF\nzFse9s05sUWPHDx8rI8I2LBu7mgVShki6o8SeO4I/Yy3Zou2o8epCrnQJ3OH\n4PAvTrvuXbhQUIwPvgKdzt8TtJfi/huFIEIEwDYIDJMCHazYYUeVT/0eUpIS\ngRmQ\r\n=pPxn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCs9bFSIKjIpUAaCwdJFiykzW6CcbCeadY8hOYQIONBWAIhAIn7/TPBSQLteWWdq9xs77okOmUebeZSxjyo3v22HYxw"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.3_1618250833513_0.019909415396895724"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.4":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.20.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"81c588ac960f556d66400f540252f2517d711748","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.4","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-WB6/V0aDUFy2/qf5WNkn8yp1qivu0lL10u/+xMMg2VV7ibxBvhP+HtNqcjQMyu5svrPt1tl8x4ZDBtbSElw/QQ==","shasum":"a3e25de113acceeaa481d05bcc7c7321a271eb74","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.4.tgz","fileCount":82,"unpackedSize":182181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdJDpCRA9TVsSAnZWagAA/fgP/3sME0rpZ2zFL+Zq3Vf5\nAEcORBRQO2K+DFANFGlgVjy3JATpj7fMyioZQvoYAgX2lSJOlAi0fst0gdH4\nLyiMZfBRCj29UmfmepmVQZ/UIWClk7/26lvUaGz3wryjHf+wCOR9XvxMxjB6\nMpJhgnimjNAOKtv06GFVwGZ/SPk8sDyBMjAvQwflZi/1XlEqC73VCizrwFQx\nGhcSNCz80cD4vZHDw+KoeJfUWjFY5TQwgFrCHV62v7XJrANMc0stXbab1ueo\nwN3PfUxNb+X8OBZ3bk49y8y7dVZijJNU2T7BtOL3CBaweshPNgjbTvkqQ1yG\ny755uwHNitUIRhYs3O+p+U9VzlcrFIUz+r8YFrKHkTWFWSoKUi331jtJDQiS\nznYHU/l8EaKSuLT3cMSVL1Xmtm7zBQ6jmzwtrSu7dFHXK6RUGmlbr8wfpYih\n5BoO8J2GjHtuZegilyB/cFEB4Rqqd/MqwkH9/v8NC0EVuXzR05kMEdIrgnac\nTXhbUgzBjmNERC+BJWP2UmEB0VyLE+A8jRlmLOMGIEJdZA2mYIxmnLLWJAaH\netjLUtj6Z765bgR1yRel5Kr1+rFvKTj+Z9NZvgfJk3q3EuPo2zvwcfPSZzVC\n23kOJ/9uBNWYO2j0Gt1Ttdtm4SXzLBHit32BBKK6IpTaEZfEwnA/506Ls75n\nose/\r\n=zHqg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbbv7m7tT9c0vvswmSbdYy4LwtuChioToPlBwJRXhXgwIhAN2poWHGAko7o3TtH2z8QMoLWYzwQqvArzsZtfY74feB"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.4_1618252009426_0.9336222222947721"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.5":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.4","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"64610da3576f7022d4005741ddad736d21f8c071","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.5","_nodeVersion":"12.22.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-L4bq7/HEIIIW2nO1y7haqb6gvGl9jJdIYPGrJn++jDpcOu4uXAB627bv+s+HOW01X11k0rVN9qw+jIB268XfXQ==","shasum":"91f3acb26607c5aafa59e2b55fbcfef80686e0e9","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.5.tgz","fileCount":82,"unpackedSize":182181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdJpfCRA9TVsSAnZWagAAJQQQAJllLIDK86anQWxttW5L\nasUPfSpJ/jRYNFDrXmw1hZS/b99TMmV/iOaY0ACoYYedrsiujuuGywfI92zA\nSMFFtSgaj5d1LBfB5R91mxuBH1iBUTjai/XPIW68SoAHX6npbEYbPY6qtIhZ\nwaxbi2YAKDO21wcpDdziJSEHM71nOG4H+yvKdvE9Z6TC5qx/Bpc8UH0E5YZm\nXtlt/DKmI7RBtnhVGwf66EUwF5X02Tu90fprnWzFWJLGoaihjsEMPNxK3tbO\nw61o5WHzYyxmgfecXJbjbn2BfATnCBBQ7+/t34omAcy7mZ/wVvuDD+FNjklv\n/+U9cVFXXKH/AUUR72LVA2vcEOIMNV12kNH4SEQXy352rPM0N4XicGf4W9fp\nTBP6zVx4wZ7oOjbrO/+UO4BfU+qpGdBl8m1WoYZKxi8+Fk8EGlBR3FrgLtzf\n9CWo8NqYl+nd/Bts9Ii6BYyPkjV0aq7eakvp5DGnnzBwRhaHQlJucm5otYl7\nu1Buf7qu7Rv6VJ/cJ/o7K0kM6vlWU9g5WN9Ph2CS3gXvE2n0OMNUFFyIlDHd\nxtxCbN0w9uYr1L0k7np7WKYD4xb5PqShELcszGBleiCPJyAE05qTbEyCDbfT\nR/Wbbt13QbUJ4sWyNz+8mMymH+oJpowkE2Ky6J7llMNNC4RpnflN1EEDX8XB\nqWVt\r\n=6PeJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+uqUVBpROFPXKi7hVKD1gRzgC1cNpEMgjIMqFv7I2yAiBY9B6higha5f58ctBsvogze+Ys/MFVa2lpwLGt8RaTFQ=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.5_1618254431113_0.5825365703370089"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.1-beta.6":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.1-beta.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://storyofams.com/public/story-of-ams-logo-small@3x.png\" alt=\"Story of AMS\" width=\"120\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\nFor the above example, a request to `api/user/123` will be handled by the `list` method if `path-to-regexp` package is not installed in your project.\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"b0defe3b42cee7a1ee871574ec565b45881d1420","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.1-beta.6","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-9kfyHYGCBldrTs8r7B8qWWoQuH0vaKzDXUEi8CSInBIxqVcrVMwnrDlaC87PoHoKrpRk1H9OKf10+jjyXJkx0Q==","shasum":"76400d15beddc17e23bd887a0dc99f378db06bcc","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.1-beta.6.tgz","fileCount":82,"unpackedSize":182181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgfXHxCRA9TVsSAnZWagAA8dIQAJzrTN0r6O2CKEHiqmCs\ngFMKfZmOnWjku60hrOpOaY4Vq1idNUel1XE5xzxBncmy15lG4bBEQdeXx6cn\nGJSjSTlZzG1k9lrIdxqK07cRvGf7S6J5iSaye8jESMzhLf8kKrvMfXukZezc\n3JYGiD7h9JWO6mt/IolgWtL6E52U91PRH5i8fPLGt6TXDOCejQUM6+4X8DQ+\nOCWbemDQX8XcKa7ZC/0y/nCx5pNYp3Wx7LJQCAO82wlkFRJDO9h8SYH3EnTz\nMfNUraT+Zdz5Lfhzw+LBtxkdmgU8p92ZCjhjPK35klddUd3h7ZYfdzfrWpv4\n9khJDPfeZEiJzx4SCUsF8bEg6/ImkZFfh7Ty6AJ0xLdpJ1P968KZXd/zJkhR\nwmw6gaPnjmWptJz/VLQ+2V5QXniv2YqL3jsL5u5j49aB6ic+KiJCMrPlYS9q\nwiAKD4CT327Z4FgutTXTnuDAlyv3mJwvaKndoYOYpuZ0wok8PoxfPDFlZEpU\n/NJVWGDnaJQlmfcKB4KTPC8zfkE++HEqG8meOnz/yXNIavuiXH3uKMu9JK5N\neaCB4uUpvX5F1hnuff5xGIrQHYafSu2DhbMOaSBQt/huup75C/Tm//U7cDN7\ngiE/oQ445Mwgmmlo/gd35BgC3MZNNljfQCqrWxq/qvlBCILrrAad3i02mylw\nCeUN\r\n=fFeE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDG8quv4knFl0tZIM0oElHJC/U2SsfJj1t/mwjo4z0G5AiBcc7Tc+389r6vdjvDLiWo3S/PGA8HIuCPcNpKPNleAWw=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.1-beta.6_1618833905228_0.02594795674236905"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"4f1d15fad0d5ab87f520ba4eef7de8b22d3062d3","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.1","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-O3TAiXe9JOI0VEZjAKxlagD37nbB9lo9Ppht7S77HT9/Muh91iM+EzV7Bn7Ihk1ZTmBDIqZ66pMxKpx+PPnxqw==","shasum":"7f3aaf6f6a9c0d0a88478e152f6b5c62aee4b490","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.1.tgz","fileCount":82,"unpackedSize":186277,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgiEyECRA9TVsSAnZWagAAersP/3Yg8tYGwPUA5pagN0sl\na7QaHD99d1zmHwW3GNysnhEjfAT2oWF4Q8jtOrEdstyvYiq+7sIc99ip+cPp\nOxBwkATGzpRJMZQ8gz7vfXtqnpQhOxmJxpJIvg7YKjknwGFENal1/r5HUXVp\ntba0L2p+CCliRxsKaEByGypaOCbK6DDIca5whdlTxm1TnpvQN/f2456+iBRl\ntZ4XTFcR2d2ajvFCri6bBwB2bcNO3SsE4FolL2VwIJZvo75IYtoPCJel3Tcp\nNEMog6rsICifugA0ZuqJKrsb/dWKPlC1CZxL60RriBG/772R8KG967V3Zhv0\nIqF7iXdFUGG0E6ETBWSqsM0DIj/eXUdNgRwyEW1KTuAAHi4N2g39oFIYhvOD\n8Guyrf8fBklVVwsAIJcMH6+9QcIQJS30VerVhp2Ab4VuGFe8kBrUCcB3K/QN\nCexXfGNGzGRjtRcnIwraFYN8vNwQywEeN3OIAxh15m0SnCR4bmcXmoAU+J0m\nJlCC7gD6SacckGsLrLnixhAMWF5SrCYY1FrtcH/D6HFSkjIRddExZTgpPAIc\nNo34/RIgmnoveQK3EyfFOFtxEQDgJKXBNFRmpoydKa3bhLi1b3WXcXW4PNQV\nsQhbv+C72q+yz/pUoVeb6bZi4B38nfmjmFXg834BANDtqpNYoyt4Q9eVYfgZ\nBE7+\r\n=eS3A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCM5VIuiZAMvWsVN40dIkUorZ7nBImfOzitLe2hlMGVpwIhAN3Ux17lkpfAFOuN7fe2d5n38s2xxEKlWIt4JUlLs/u5"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.1_1619545220277_0.6817247902813313"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.1.3"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"94c8c60a5329a1dc89ce395a03059c7efebbaccc","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.2","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-XOERthxO6VUO85if4r2xVSAxZ4/wniZTh716qZN87GV6GbMPaR90CMBjWuwoY87ju7RD9zp2AwztMa64m2YeZA==","shasum":"2a5598dde7e229281a83faee04d1202dfad4cc4c","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.2.tgz","fileCount":82,"unpackedSize":74134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgiqrCCRA9TVsSAnZWagAAvLAP/jkAFKIOsrZMsV7jeZqY\n+rXHwylLO2DxlCp2ioZ1fHnM3UFZ4GUoa9kUTX0SAFeE+4sM7+WRA0BEmkPL\noY2t/nz/IL3c+1vpCDa1NBFGs5bKnf85ajKzRpnmZDha7CiYSSI0kWmIfUZP\n0AZRBrIyVkT398rn8gAow1STSvbMbpJ4yBbBuqO/svIkaNwynLPqiPoqBFdS\n9hueai1oG7jyGS96kAxXLjZl61zTktZG1mLBqr0T5vhwNSrWMhGZGSrkRblw\n7iWtCO8WfwothcUipk4ZC0EXqd8gpRVDnEvskH54NtAX9oKPx70VTMmt4peA\nPVAFotl4Ke9+8fCintVtW3Ex4ZbElQKYCe4psEIav0U4Kl7BDNeTOdvmzh1n\nOXPvy95jcUQxPuOBbpUP3nbWhjQ+58iQWzuaoRMWrRYXPw9UziG1z7xCNU3Z\npzKQ3X8hrwGm/8ogWimk5IP5b4wYuEXq3YHQZbOXZIDo3nBflHUtykRl3Pa1\naqDPG2U3Fto5+PhKJCO9rOUM5YQqPFCmDkGh7l9YPqbjyKuq7adEor9Qrjrl\nP0Ptp+2w61rX8Zekv3WjZifj1VihqdbfwRJ27ilTpZNZbo6qu+NoSDdYAAIP\nMCYhdVSnYnNKFkpd40m/WasRzS4shYqR6PposzmUpzCb1KuGDMFCQ5QbjbMv\nJTcW\r\n=Bg+K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3K5CxZg9MBcVDBsu0XLylF0bxbSaVofqamjC8VqDhbAIhAJ4K7RIzPEpfn/J0+Bp++CHMoFT1hWF9Jk0cZ5EHJpOf"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.2_1619700417983_0.42983929436627033"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.1.3"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"e3aaeba15eb3a7f38577a1cf47b5d6c51bae6fe7","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.3","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-hpa6gxlmZPHOR+xFuvAMIV094z8fBNgJizDkcAYpHG7AEXzTSXCeaxnrMJUsdCXtHght3xDvFJvw0V2tlcW/vw==","shasum":"cb9d209c2189467ffb45beb66bc65411a110db99","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.3.tgz","fileCount":82,"unpackedSize":74134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgj92gCRA9TVsSAnZWagAA43QQAIl38Jlv0bl0FC9T07XS\nvCIl6qGvNUUEdnxnio8QVnfU3Yr66vqLa4MDZ4XHwCzjz2ryDf300YfY9ewI\nDEuWnahYckHzxA/LYQVHYVYx6m6FDb7wUYYG8rVqwMkf6hLySP0bb6fO7yfN\nPm3yp6reF5Mc+m7+cwRa9I75DJ/oLnN3kAfXWGpwsvcjOQReq7Zt+f+A0/gl\nx6ZjxKBTFUlbTGSLge5zvufpPOiZ+nQ9/eRtfM6XTllRJ0IgBdD71rJ3n/YQ\nGFQ9LMYDRgIEexHJT9EHX1vEORtMPpYwFoVymG8WuCRey01P06AmiufMzCJ8\nAuaclZaPtycHEU4geAQiAessZwPUroOEI4nKKQH/GVtZiO1UZHpMx7Y5bPjD\ninQdBI/b//ZVy1mcU623YbqdVyXeQzwzP/H+wY2l9oDPIfaLrrM0l3VGnqzc\npYTmlfDgsDRCUNsVAvYimW+3pd/ygTW81u+uYg8RDfbxRdSq+3J9XzxCPYVE\nGQdJZJx7YwAGlRd6/XhRZk+0v9cXXKu1yHZZREosOZ5lZFzj8Z1YhdLYlcrd\nI3eiPAyHpsfPX/kgSsXg+OKvXk0SAfYT+uQ7Ssyk7mKmJTaGQ9wB83m5Q7mA\npInIcrv31hYt0osm2e9iGCy4RuxHdfELvaMsEXOkMSHKc/kQwWA3ToMuoXO1\ntn5P\r\n=jCEU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoGNEXPtHzh2Q2dbJ38vAqigOg+4mJU/aAOchdCFcgAgIhAKSRdtbDqE2pJ3oRjOJkg+xLJ/AuKQojfWCu3u2f5KE5"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.3_1620041119985_0.4978204084058184"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.4":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"89baa107f23221f9929b00be683c9ba889c83f1d","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.4","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-5K/18DlP3we82RrvsvCKhqBKBqAqCNSqYWveSsfFPaZWzgvE3ZNbOpZ1/dtRLsaGBE+qJaVZnoychNMK1u3hrQ==","shasum":"883e9207a5f56a98387a7780e5109c1b4369bc9d","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.4.tgz","fileCount":82,"unpackedSize":74134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgj96VCRA9TVsSAnZWagAAlRwP/3yM9I9cFb1obuq8NYv1\n4y0ZbtOVBxaWfHuvsjpXoA3Fm80hBRMPgTx7s2uczkhEfu1hX9cvAq96ohMl\nb3fUhz+3fdZjYkoJl8/Zh/Dc8OaFP/Kv4GT2ZgnJ1ffJ+JIjgVLjiCZGEzr9\n4Tu2Dchv8noCPzPFI2g0+HTVWQaYGhmW6uVgSKrqAvNdSmYjVyOUtNa6/nye\nMkPhq9v/TrVM009nk8Tidr8r2w+Pj65ckmqhpsnViCNHwuIZrIqOtfrnR/4E\n45zf8uniCcG7NXZ4oCOgvCSCx9PEVt67atDGDXXtk5C1ZvqbFCGsrasgElEL\n8/AgNBQsAsBHHA/BK8EqaDAbXzqpeUDFD0SsUE54oWsDB2eV1oKa1K45oV80\n/IpPyJLoaTXIXG6jDq2vtVoRbHclGoKQ7zjbURK1x6tgg24KrDlFLn4Grnev\n9B12lARuBp7CJTIevD+XOMPt10fUclW4c68/OwgpE+gvd99nKh4xTu1myDcs\nyoFRrYTtfcq4TfUYZsC1Im8fLAYs6iZgVEbD3KcJTmkAio5EYnHqVfC8pTQ4\n5JuURSyta3lzy66WEC14uYh4lJ/INobpcApKJM2BDigFM15MeDTvvgbDbGgm\nrhEp4xI7wSsfQ51nG3FhErxLVVVlaEGaKVjYQcjjB5W8ZdFHTfIah4J5zGE7\n6KXS\r\n=UTRz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzByJ+c9A3w5TkvLehMy+mRPo9KySGKA7DXm+Zj22c6gIgJg3ljdynmgPMZHbifLE05hVfMfZh3r4ZYRGfw7gThos="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.4_1620041364792_0.8203099105893421"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.5":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"fb3e3235d8d195214b212d3d02adab95fbe2d0b5","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.5","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-BQ3P43DL+uhLN35p/C0jBxTuouYYha6WoxTL+Jspx142GZugBTAN6fMLl1O6mGtBsFvIzEVM1IsrNEjOJbsQ+w==","shasum":"9f905c79c59761ca76bcef05a37eab08c2f12e6a","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.5.tgz","fileCount":82,"unpackedSize":74134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkBPmCRA9TVsSAnZWagAAn7EP/2t1puw6jKcLrVclidLH\n01zsv418LDWZvGSu1o9pitc3V36A68PnGZSkte5VqDyLMkhYM9yq584ewczM\nkpwnxW0gmpYOzMJf5oMa8CxV0S+OKebUiVNFZAq4ohNYruSTgrNsypCpB8U2\nViVui7fkEVO7bksD4KG5Q5QcEJ47vDeAr9iH7wum3YNKBfXLGH0YJnwjRrUs\ntua0jiSCMAynatFfBQmm3oCLQ8xR+jUXB+GtIPsc2xEYQ1rwrD7G5G5cgNBh\nDHZjh0kArt0c1C8F7UAA8S8VNDevUXXRT2aFm2PaciKuolsQtSdTScG+8QaR\nV2A3d6WmqN2ZgCtUu/7TTXb28fKWM3kF2mz1EiQQMwLSfPav5NsZ8SOpkYEx\nxnJUHNpMsojk4myX/xBCMbuSE0w9Cc7pyOcJWy9J02tK8yzeIciCMu4TqZI3\n1fHIRzje813uFA5WYu9ErK/sMpXgOfvpku7np+iXwCX65pIPCjrTbYi5CIGk\nXZC1Iuw/LR/Ik28GCT1ZsGiNDxVY5SlrL6MZtDf9rxGUciTkdVXe8nvek8IC\nWrDkXQWPa93M90x1whdrGu7NRXn0q+JmPial1dZTiTnrtsPkal4/egER6rUi\n9bCWO3ANDfzQNFce0wwIt79kpUVjte//+cUVLR5uLVj4vZOEK3DYqbUhQitQ\n12xx\r\n=XzIu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC5cl3jPi7jik89G/+B0Hi7j5MFJiBxIF2QQYUX/5Q4GAiEAg2GoAwpf8vfkOH5qXrP17gZFAjycPK3bq1cIl12uHJk="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.5_1620055014187_0.22308220618222907"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.6":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","multer":"^1.4.2","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"8ead3fed4cfbb9942a49415beec55638a76bdf8f","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.6","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-KK8MD9/DE5R3sc2sAJtXzW8lsKq9nKbh0ODhwzyjlk30llqKNcLu7nqp8+tc2mBdpSoIMJLiCcbUOvpDKA5mPw==","shasum":"74368d11e9a61cba0da73608de389491efc9a29f","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.6.tgz","fileCount":88,"unpackedSize":82705,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkBWGCRA9TVsSAnZWagAAkq0P/1NMm7HIiVQcO+4kqOvw\nPFxIvyQatMJoBtz5/UrbyWFCHQr4usZBq4pYfnEh/8T3pDicdS9lCyW41aJy\nW/G5a2hDeREVgb/Z+52cX826pjZHi+RkCKlstLMzBysR6QC3JVIWzMqlBw8v\nkNol0/shBueu1NN93rkSGOySK0aoJnKk8hBAhng7zfEa0srnnUTcoJVhFW62\nfJJBGYH8aDkRHn9UusjwdH6JiLAL6IQymt69BO62bMdp/kPvGklWmBdmeDUv\nPZ+/0iV023NViYMIp8M551FSEUD4sa2mSnrQPQmLf90Y4rb1f6yw9NVULTuM\nEkobotHy0u/a2kfnHx0nG1zw+eosBKg3PGGXc63EV30O6fl8+PRSxMisMwDN\ntRIHepc83DY3cl7HIS8exikFVhw6D8MOZ4P9ETiuiV1xd3X17MJlrPicmyJ3\nrOwilUUdmHfjJ6OGt0JwJ/LKDnxxlxpU+Ikg0SljAQLgV/zd/vRFroGwMd0F\n8BPOOE6o/OATH6V/loAhP07aKxje/xPH1Af/dd0WXBy5zePyzst57abX/Ikt\nbvtpvUol27wO8KK2XNDFrAZzQ0OJYOuOg0CNzzuhbf8hmWk2Dsu31ncGVuox\nn81+fvqD4Ahma7NygdpuPNb+MiAaPu7mtdSWceAiGLSYFy+yojm9zG6iaS+G\ncrGM\r\n=i/Kn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCRxcbPxTMe0ApEuvtIPSO8+UKkDDDMLx0e+twczCu6lgIgcRcla9CtgCAbVWvVvejaRAJHn/6k+ZI6qw8jPrGo+IQ="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.6_1620055429598_0.7331442565968569"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"695b99355630c67e2392e1475a3b804fe9adc9fa","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.2","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-P02sA8bltZlSdYz0XM9RsWUG9g6pPn2SXdrFWPLkJtYWe3+CwT9ujNPJ7o8ruwccRnbFKVRewJVOFzDYMaEZ5A==","shasum":"56e913d3f05d7ff40287ffaa0149fbcc4459c62a","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.2.tgz","fileCount":82,"unpackedSize":182817,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkDORCRA9TVsSAnZWagAA6ygP/0Zbb7VWTpnOhGDfjlzu\nBAbMfBDXQW63N6E5GP80cKlYGc9SENZFG9QFPIi964V+0nehmCGfaC4KbRWz\nOkKTPjXmNncbHe+tcVQs3BYQGK0RDpghpvjv2gINaUDAbiIHQDP3HCVUVANa\n5rfw7TeurViXdT+boBdsI/q0W4IbGKhzajG4MhSvygbuk89PVWOr4B7zbp+P\nMhpIgLm0YdwvhEHy6eSgM5SK+6K3Wf3671xE++tdUJ1JBnyRzPm3y901Xvqs\ngMQi/iRccm+Lx2XV7e5xF8FnaXPmRYx3CBs9e04SusoOmTzgoXDYPNLLRR6i\nAXWtu/BBSZLQ3EH+PrrgZCtUJp9H3TS4gXB7PgJfIfsKe32ffN8T1cGbe8Q+\nnZi/stw1eTtynDdtfqv+hKXkDmUltkPfGM7X55fWrnUb8Dn+SR16A0WEJ/JK\nQvajt5B5GewbekANJ2E6Aa1imBvh+hDRzBzDhp4a2JwXDM0Bi9pnQ+kSp3qU\nmhAp5yO9bSNDRJZdgC3USZs3+7DR4yEzL9XOKKCghMY3Fs9ikYptsuNYGaNd\nqUrCvU2kALf9dr1xSKUMHnVGjoflDMkjL6dT1TfhklY54nP/4QYLF2SHkRXO\nLtdLk387ojXb+Rfks4eLIqcGwMdO6KwuNLqTSkU6WcUSDhnDLGB7g9FbjfbH\nP7ww\r\n=5mSS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE9M2ODcBLuUY7awnFLZE73p8JGd8Sk5AXWn3YZ9tDNcAiEAnvh9+CV5L+CcHXxuU5M6OhTIe/S/Y0hEwQJHaEJnCzk="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.2_1620063120810_0.8187383212171575"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.7":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.7","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.1","@typescript-eslint/parser":"4.22.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"28d34cd6dd6a42f9ab52d2eff0031caf3a0aeb57","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.7","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-vRlAkNJWUVcIqm7CJlgOQyeVAnEsexY4scnPq0qW5s+ZyGTyNwZASfFpf2+rcdlGGr49P0ioMjJhRl5AdI4wuw==","shasum":"bc15d63de84c276e7d5697a93c7c42febff9d9fa","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.7.tgz","fileCount":88,"unpackedSize":82041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmYlnCRA9TVsSAnZWagAAVLsP/i5wMeOHWLVC4Fsia1VB\nzhVXNOKODD6XCMoPk2HbpLKqDpD6xuaxh8GKKq0YpTBGkRX3NahaIVG3ByI1\nIw0DL3QuJkAh1xlioa++rPJV53PPnVo5H7OlXZTAS/9DFlMmUgWsuVi4b0+r\nLWNO+JLOL280/ICdIiI0htUOSJTa0Ht4FAxcO95ep7Dy1F7QBI21YL8mnima\ndKqxxkvadVVnMTEPiy8ra8EWc1x34H9cLms/G1odNTnrgN6PGxswOWrk6IV6\nRqbIfGyfOVkLibD0w4WqkTmNA8vuH8q5/OEhPSV+brnDFiQmdBbmTKG/XzOk\n6xhNMyRkh+9uHkelo9bpE/IL/RtgGbCSMPrcVxEkeHM9lPk3tU3GsOkEoH8N\n4NUwTfd1Ffmj+91lvEFfxIlUyW0RkceJ0n8PAXJvZuVzl6Rvq150HCZRL9U9\nUTYraGHRueir5PXj+QPpAPNqvGT0s/C8HJfGuwn02wy0+VNuZ1Tv4qAAa9Wt\nArQBbC392sGp/EqN95c0/MZ2p/0Tj1qwTuC4QICgan0TKW/dnFMNUhnIJfVe\nb5AQMcmKVXnfQRoMlGUGKjFi6xtlCX3YUaI6XU5CtZwuvr2+75OHk5YKIjmR\n402/INqZa5kzec/BQMBYjQ40ZRk3xNYpD5wnAeyqLvYgxEbCU9PhhWbRsq6S\nFrrH\r\n=Tm9d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHJGc2x/mkSn3eLgxd/AyItvrOFcezUoZPs1CRJfOXrGAiB+2H9YbXdEW232niUDWqLiEhUSlf9ILR8jU1mG7dLfHQ=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.7_1620674918671_0.3761869212077691"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.8":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.8","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.24.0","@typescript-eslint/parser":"4.24.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"dc4c1f5ead2fae56f4ee0f0838087e8e60cba229","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.8","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-7EYpSRLYLIOJTgzIRHUnd1YQ6ixTfb6kLMtsGCYa8kL4FBh7hJ/IJQdYLgJF0fExvGyEU76wkWSDQnQXcJ9KBA==","shasum":"9fbef672f9157bf6b5ac58fb0751f6256c7217bc","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.8.tgz","fileCount":88,"unpackedSize":82041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp5LzCRA9TVsSAnZWagAAlUsP/jK4KuXpxc7EFtc7XvhM\nkRD7+/fRck3mwuMOy+KZ1MIF9wpPlC0Ageqxjry+AjODfm9uTPNjV2GzhTIk\ncIov+ahfdLRSH4p1fyXJU48tk/6aJTXDnjRpLRnld8HFtjfTyETB4MWktcal\nHRUIHRro79SbTPXOwlN0iQDLM/gVtICM+j5HX24IuRqE/R9sZPN4yVr6N6nN\nUnwG48ZEKvU4KsUvRaMYZz8MmcxeDYcUnfjNu7T/RDJZ3sQvccUaLuLTCoXF\n9siCwA2y9+O+w3t/JDI3JRlFD3Ih1N43aQ5vhvVudQw6rbo2hp0v5hsTK8Dh\nqhAUIE+haQHJA0X2kvM7J19IU2O8/i7XNIHBHLfDfB856/L3VCe2LNyu3CZS\n1vETQMNzBKxN0L5TEuHTCOgnPlymC9fgw05U4BevoW/E5RqWA0NEknXZDg9R\njU02RQX3+OhEH3ctq0jk4se2B9Xki17jP17dFr57IfwtGsfNmqjYq0kW7ET+\n7LAt7+W37x9QFoPW26mDR6o9ztpB6VQgQ8yb4gySpTT9JJ214FQ9B/W4ZcHY\nNa4zgernvpst5BFDpJZgo3yBkaXYxgHgllcHm3Qo2ZxAnxX6BloaKOPA01Cd\n+phci/MVm8ULrxUe6HknS5m+E5BR5FjQbmJOQuotJXzskP17nRE1ecxoWa8l\n8z7H\r\n=BbLV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4JLBX/9qZFBcjv3JXtuquzN6W67u1gRLkKOqQtcDKGQIhAMYdkRkDzLj1jaDE8U9nLN0i2YuCwlWJIFg2QCV419Oz"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.8_1621594867009_0.919420589393803"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"817b3411ea701a0dda0a65cbecf6d6d99761bb9c","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.3","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-Isjlf/5i/tWGNrih4CVipvqg3wzzZJc5oXr8lUvest1YljdbjqK+oIZ9LMgjFCAwyvd0m1otsMuCEPGMv0buzA==","shasum":"4c678065202a1cd629fa97b82d3851ee42b5d2c7","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.3.tgz","fileCount":82,"unpackedSize":182664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgp5NPCRA9TVsSAnZWagAAfEgQAJI/RMotaknhS3pboUSr\npquZRvjmuoC6/cs1g7y6YdD+DUwdFdlLSldI1tKaRNrrd5LjK/AMROYxSoq+\nw4OXNClpAwU2CrFD3h1cwdpVmXePBTb15Qh3XnfDXymRom71C1hZyW4KiB/B\nWYrQbnISKEQaTOCnobIRC+uOe64D4yM+GMyOhxeAWcWaylmvSWj4vogRLM+N\njKhmmb/Q7mWOXoFPBO34zFcps8lSE+VJ7O3zPaC6DT9PxVAwPWaFE9z1lD5x\nZUn26x/SC1AlCUx/9X8R2hSJnOzK+eKGjoYKPMkCbMMB3Q5YmL4ltjSn9oG5\nzFOWBp08sQnBI49kxPA2aEsNUrqjLMuHL2cWo0ecSkfAiwW/6nHYuOG+wbdU\nehBMqHauhFnGnVBX2kk7S3N95CGFn69YizhA4jNJ1Vcr0iaEPI8M0s+IjhQo\nF+g8WRM4JFDeH0IhJL0paudWCj7kw33V8RZO+kM6IQ0xQMUkY2/goRKmI0T8\nGq9VsiiKN39+zVmkCgvK4431Af1w1guM5xgLRUI7ffEMNY+RkWBW8D4Zg88+\nlP191nqIB2OsE8SQDL4iiEk0o6hjYnB9y0z2Iiq+qr10A+4WF3De9CzmnllS\nJS5RmRypvp2EBCioPtLuO4cvqm+5h2RPye7NnkUcuFquKNXFOCpZOuusgahN\ncjWF\r\n=jI9U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwhrBs80AyKrNNF/UeWRsRcPIQsZyLkpsQozNuKKTmvQIgWVkooqTKeeXVfeuss4Y0G1itrnYqvNXQ1XlMgla7wrw="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.3_1621594958892_0.6145217368849953"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.9":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.9","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.24.0","@typescript-eslint/parser":"4.24.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"e4a9dc504a2ee1133e8f04f33e27f094970bd11c","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.9","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-hKEfqZ2Gqn1mD25FA/+XoJadCKjBzwwzdGWMZKWpa5bbuZLK6Ud2+8qsp41wS6Gj2jiqoSja/x2Rt3AVr+nuZg==","shasum":"7ba906f0733f7715ca4cb3434d9f7d1761ea6042","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.9.tgz","fileCount":88,"unpackedSize":82304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgqYMZCRA9TVsSAnZWagAA7Y0P/jb3LHkVXgLKsC6rBrdt\ni4h5nTkuM2phEXT9rNI6sczD9D0klqqPM+qUOtcZq1O6W0lCd436oi4Eqjkf\n7vuo2KIyG1Ssg+WJQrPOV1iarbCFNzBy+SXxv9O0IJwq37SDI5rNfmYYzwPN\n6BBfPf6O/wYxbChSB2zWRwczy16wXMQX2IAAn0ZxManOpPmqO5mIG/ocOdwe\nIbznvmiFMN9IQhP1DtsfzAg6HK4JqKYBznX4KR+0tmAZewEgfBVbRNxmJ3wS\nyZ0RGhWe3nR4KTMa9lNWc868fDJ9CGMP+cTzPEjCfxWCCGCl6N38h7CUynGp\nEDDlOCpCDDOQkcEr0fe2HtsYhlmE04x8/+Z7WHbNc4GIgFZ33/S2XmjDNHSB\nIQxXSlmgsjJw4MS29Lq7ZvMnPZNVlgB285Vmi6w2NnuZzTkmg+koKugIUclw\noXwNBga8MYwVj/KbAJMH6aRjLBWIhXLlPxt609WRvytAsrYCHc1Qoq3X1mMD\nRxM/zg7qvm3rLisHNIpLJRylO6WHKt8QRjC35PdGntg7b+hCuMEZHpAnZmkD\nOh9jhW2n+ZPV8vQdvMNi/4nlz9HgyW8eoq28vqslNcLo8G66UG1TI8W4VCSG\nI/gAoGZ8Jpzi8w2wW+4SM1Y6RhnmAeZ8FnfXzBKSdPLRkDQm02ITL3pPolVD\nvHVP\r\n=QVR6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICcCytyb50XNAovYdFDv9+Y4+6v5pCcrlRBMIvlAzCm4AiAE0MlZROVeG56lVnm3aN6V48HR7gSaqgDp5csYlAuNfA=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.9_1621721880794_0.6900681898474232"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.10":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.10","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.2"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.24.0","@typescript-eslint/parser":"4.24.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"3cb2ca4b8a23f86c59165cf05a951a994f100b9f","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.10","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-5h0BwBC8W0xeOwCIzBFdN2maRatacjtmMa1TvqrN6p2a/oNOaSoWwVCPQkK6Q1bWZyj2NsLemQ0a3Hv8AHvoRQ==","shasum":"5b0ffffb8db6fbd0fa85dc2b71315cd9e089b7da","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.10.tgz","fileCount":88,"unpackedSize":82154,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgq39PCRA9TVsSAnZWagAASlQP+wbj3w506Ou+CIz32Fpk\nhZkbFJ0xMEQ6aZsxYFYgtP1VVDlw1oi18tgjlIYdHMw5Pg4EkRcGJKOUUGJm\na6+bSDdwzzL7N5bQWN2zSI6ZmCpShzqx/9lmfLgFvJg25lhTBAI2SSSwLneK\n1Uo9mucQL5TTyXsZias11RLAFA52/nH2coWajFfbXjlClfkQS92s6AFdse5U\nnIMVR6x6mZ1GAORqd0eMruytGPiuF28ERXAIoEH+MbeDxMvnAkWyoJfGcspL\nqdsR/eJpQtKPicLMB+SmzNQs93wTg19eqMRJGsyRHgNwJGCCrA9dDcXNqg4x\n/9z1ThciT2V3Qcl442h+Eq64YQo2ZxlZJZO8S3dIrnqNa/LY/t5ajUpKNdIN\ndrmmRExV8cC/T/bl3m/W4gwh0Kra2i73bHpA2RMmRuLN2y9BXgEfYpr9nM7A\nYE8fsUvWmqf68OXrGEEl1ly2nDBHn5bv9Sq5rzD/VPySzv67LONEXB7YwmGD\nH6lbUJXDLlZkQOU/4U+wpwDSfq3Cq9zaRkZnscsokKsq/7o0UL8hheozA5nf\nvt2BBrRkeL+CmeOUDL3o93bY0UAwYKJY17LPfJQ329Cko8Okpu0O49g/VsM7\nFu3tg/m2sCYLD6TO+6FlZgZcMQ3Aml4hvEkhLdDmTjNqF8F30Dg2ufK/SsWq\n83gD\r\n=Z2jP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCubPu/kLdMzG42ydcInurJ4APu+eBZRBmX3U5UC5KSBQIgSkWdH+9SOJyVgrHGwme47jTaIBkNKjEYrwnqLQF4XFI="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.10_1621851982857_0.9636427632454989"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.11":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.11","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.3"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.25.0","@typescript-eslint/parser":"4.25.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"de9e8f33b71a346637b1f918db7684cc180d8e63","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.11","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-Ovdw0fCUR0NaqR+n5SEExYr+B2RPZYQ6tvD1FmVXDAIOHtiqyciPKw4BcMxLGOtsa4zznvRHuvjfs2DAhwn+6Q==","shasum":"df964a19179dc01b4722117be9edfe38e2caef52","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.11.tgz","fileCount":88,"unpackedSize":82155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgtKqTCRA9TVsSAnZWagAA1gkP/RvnDyENMWq8klZXUk2V\n+ko0vtKhwRbOLqiqK/sPpmApub9UdVGprWIq39/6u+IGG4CeDQT791SV3JjG\nUXbIVZrqadPO4JmdT6/ui3qYQu4uTtHDkKdzXiHBOePK4VCLbQlOW6opNX3p\nEfH+lLPBXWFs86ea9bm/fYcwM4Y0Jxa/72+SxHwHQNAh9pNB0f+6o+MgIgRG\neSLoE5B+iB3UgwMlj2V+yyP/+07X1cTMzVh847wp+/namc9zTWRUaaK/aFak\n0RHiV4faPgIwgzFYr2wP62eyJ9GAuQDBJ2wTatdylLaV8l6GoK5OvwdfJJvt\nXNVzpMq7BmJ6xhJo1LE0XxF3QQHZj8Sc4k0gE3dgEXEhWJeif6cv5RA/ecsi\nt3FBxKye+3gRl8lXh7dvw+QyH+1zPeIz/aYlQUArOrc1nPJnhzvDCR6uI1Q+\neXR4za/tup2u9HJxAyPAYxgUeuI02kXHcfA3MU38QO4xkQvPtKzm3doIl8Gu\n4N/qGpZviSqnqZB54SlhgcihNh+7V69p6XOru83XjzZXjmMlAbC9lRWIE2GG\nkjViIGLMrJcWKdJ1L+vKTvQqWMj9KslmxrCxaLvxyxfkUFzQXiOM9jtdy3Fg\n1QsgrkejzkyLXxHgIz6XFgBU+RmLrCP+LE6qjlDARI4mR81yRzEhpBM2brxL\nYDDw\r\n=MU4X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFJtqb57VEaTt534BTkoyNKuLcYrzB5ikW43gxhh8o8pAiEAoDEj2vFAnmxdQ84XYds92gmn43/cNy8viqsm2f6DiEo="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.11_1622452882899_0.4523052043643614"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.12":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.12","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.3"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.26.1","@typescript-eslint/parser":"4.26.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"57ea0ad66686cff7c192f0e394179dd8d0f916ac","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.12","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-9QkHJls0PVv+c1rKTWfAxRFXw6KCCb7vzXcbhChPtik2pfwc0PWmPV6zEKySNOoULseCz1TrLhcK4hAGa81RJA==","shasum":"e5b5d1e62bdb551ca29fb8a721a0e3427b5d35f7","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.12.tgz","fileCount":88,"unpackedSize":82155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvpekCRA9TVsSAnZWagAA7vUP/3ugkKjWFNtlwl72MfcQ\noqaQ/mIjrIEOW21XFVGFVrWfKU6dyAcfSGNCc+EjEpTRVa/lTu3vWOfPCriX\nX1fahpgUQY5gaAtDAgcmI5a35ECWTdTFN0+en3zQtXL/YWhHzynbDPzFTP0h\njC9Zk/KELpcIQZLAjMeNYdBsPsADFJuZkqjTdEwpHSgSgRv3jc4GQsM4grHE\n9+p85zpaH61FgbM9eqPQEkIlb+K2obF9dOF2j2AIFgiseoKbhVBzCTap1lYL\n+Pw4rfiyDQgSXj3vLPkq7MgrTE5s/+aBtrR8eGgiciCvOjWZ5bcY3eEW1opS\nHiUUnQ47NCUow7haTQvVuuJAbwIHzHOoC1m4yD+3zOwuMHNG61FF9l+XTLyH\nAcnEDvV69AYnRPpZhw0HpIkTLMy50yQivwjEC19m1k1ZW6mUnZQTTAeO90zL\n+Yf2Zwr5utAwA8qsmPMjiU43lzpOtT5LRigZCWNcDt4h06nt7J7gaKe0buNN\nlY8J1KF+s7Ko+N6d46UceYuRKXoWgAA63WwcWguZFnT1pym32z4lii9CIKwD\nNDJ8CHipnezLGGXqrp17xKjBgWqkSUGTFMFwAvnO4VgfCs17pJqflgLck1jE\nKIu2heDfFJ0mom2l+4CpnXGL0yD3gQeAxmuebqHBTuWfGWeEj8Tc5+5WnWCR\nnooL\r\n=hf8p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC7Nyan4v+8cNnM9DsE9LFsyOA/pkXMdVNongvWuwk6PAIgENXcJXbBMf+3xBkwkETEpPalUi9v3QvdgA3mJUY1VHc="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.12_1623103396627_0.8175459059499146"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.4":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"3c31c8669f489e88bd4ed991425e3a711281cfbd","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.4","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-0aVChAp+MBnCDxfroVcfwQXGpyEzGziSR3QtUNeky8Z1C+C23xiR8yIN1eLrJbGh1SEHzKQ+aQm/ZolGcb9ArQ==","shasum":"b711b07c648713e55aa35a2254635c4b3c231fe0","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.4.tgz","fileCount":82,"unpackedSize":182664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgvyyFCRA9TVsSAnZWagAAgo8QAISFsxXAODOAPCsc3bpD\nxPxrA0UvLyJEMe1En/pX2Cj7iWcbX61IDA87kMyIfDmvdhhqU6OhNc0QNXV4\nkULCWOZ1fBM9gZypJLs4a3UKXi3qihvOkM7ZIjAW9s6PXTaaXyAQtPr97Jzg\nhZHTz/vbjbrOeE/jxoHY6tqAhc5IVmlND3zJTR5zEGwejGfnnpbXUCGb0RFr\nrRTG1aDQPraKIE154hfRXA5OtQZHNXBuDmOboAqqM+aYGEA1vvxJZmxkBKxZ\nXRm0cbQcHA3VVa6yctnnT9rppA8v2rrkVanQWlWDA2AD2d/W4J6a4XuSgPPL\n6N/Eia5KgfFs0uLG5bI7rlygOPANSUp5F4CrfuAWN7rPbEj1zwb+nIjfBB24\nOD500YbYayg+Dx9SnXR0b1fYEX8k6R62QOzhnCA6+D2Jz8O6nPUGLzX6UpD/\nJEKzQMh476co9lmrabrjD1Px+JnsVQd42gFBQZ36vMbKi0BO1CbYfXcXsrU+\n3RF5kc38ZFzoOXprJCiNcc0kJ2kgw2sqtKJqeICFBTRu+dEZBAWI8rkBDqmw\nctXgpCcNL9YZHGNB13G3tQCUbx1ahOfOCRl9xVFRbeTuAyjiP3XrbhWaiQux\n74c1PGlzUEObzpuoVA0sTsOLk4xHoXIHb0V2yPfsS+CZ7nxSo9ePocKUDhtM\nIlbX\r\n=OHNn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA2KwJ5RSnYmIMDofnvR7xtW6z63/O2u60iP9sZxylUCAiEAnrLjRfBatzIE8mUYKVycM4rsG9WZl5mWa8ArHDCb/Pc="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.4_1623141509775_0.9654691723282742"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.5":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"605c0c68eccd86c7955400fa471a676845922611","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.5","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-aqtcdNNkDcvkK86/gyycPtaHSEm+1c2+dbY64EbbyURzAjltzcVIuRHoGfg8b/tghT2LH2E+4f+Zl30USkYXpA==","shasum":"1e08404b2eea9909bbabbbc2b41509d7a5931ca8","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.5.tgz","fileCount":82,"unpackedSize":182664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwHOBCRA9TVsSAnZWagAAU9AP/RYCWmjCEI7MRvvficzm\nrjBb4IZdqCd9ZIj9iCUFtLYPer/lTX6Vn5Fsq9nfE296IOiWh96tzBtvf8Lp\ngNdXUZTLSLNbVfy2xdq/HGSyeQf/UNgpr1SIqtTfM/inmgOinsSmPpPwi/g1\nLEpvXpe4l8Wwt75LxxDU1NFD16GNwGjK5XpK/hJXLj81jJmdVhppB9pOJFcV\n36jwEFmIt2gzsUsantTR1o7OXsHVBdtzfgcOL1EyC3NNwjJsez/N9LOe5XJY\nT1szuHvx8R/rGSTjvMZKDQdbX+/GfiEDLQPttvGzMEc80SOf/93m79fVU28V\ndp8L/f1EIVk9fNRgu+O4bALHu4qFithzWEb9QGa/R4r6WJENLdtt9FxK1DNC\nEjxFkiS9244hG4TXR2WraHg9Qr5xvLBnrEe/uljQWKS7UyuhBJKm4J29rkud\nPz0AJyEW+N7PfdGL/oMyXHPpY9xDPeDMkjmnvJ1AFboqWFzzFItX8MDP59D+\nr8LtGtEQkT44nwmFFsKXYIKP+WcGUElOxz8bT11VlvO1O3+e/GzoX2Y60t9E\nlWuC37tq2xGUQzCQBEBrVfurIp1RcjNNhebe+v3TaFky0CSf6irfzb7E6Xz2\nhJZ4WIsd+stcT2h2w2LkKbrp2HJ5H4zq1YOSjk8X3t23bS92zpYZMYaBAbMo\nNXds\r\n=qNBU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDLkzLIj3RuXVYsrDPmC/DGApMCQeRQlO8QksoXEEB1qAiEApaG9vYEfXTScUUSXuvXw1ZXiGxXh8B96Kv9vfkFkG8M="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.5_1623225216980_0.16297771732152122"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.6":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"7.13.15","babel-plugin-parameter-decorator":"1.0.16","next":"10.1.3"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"c5173d9fb7e8b3c751a698ed8b70511ea7522378","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.6","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-51B5+eoY1Tee5uU+ilmT4wyY0AdWnSrJby+4MkFsEjmTNGaTnC+Pz9QVTIcSiKMrt5O+iYvJxi1eSqOKp71vNw==","shasum":"7507fa9e9f2430fa765583641a12dacb7c28b380","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.6.tgz","fileCount":82,"unpackedSize":182664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwHP2CRA9TVsSAnZWagAAwS8P/1NF2w8hyJiOW/oHvO0y\nZK8omnZxrKLTd0bgvL7TGDQBY/TROh9r0vXqo+CKQW/WI8cOFRor50JGSl0r\nfZ1z3czdbD0z4m0P0+gzSKLteFVIlXs1SVJYa7FbWxYkRFGUxnIEcozc4Mz1\n1y3WNDBtHPOm4gmAFzuOl1dFqxy4kVbgwmsX+krPQIZBeklVqrLLoipnJ7b6\n8hSLz6aGD0eBWvJc1ew8ARVOblDvCAUx/fX+0XsieeAYujziwQFNdwjkn6+C\nVYP5WuSKsfyaVlYwuw6de39/3TGgHi6l4SWodQQywzSV30WjFGx8hY2+QYic\n0NbmHhToRH458gJyEQK/DLiX1SRUevy06evKfV5vs707OAY2H+Srm2TTo2XF\nHVMX/953ikHOfCTmbCkoVP87x8HlnuBiHuI/GPTeadhNrLJa6+F5qZZSBDOg\nqno1j1L6e+5AWi3QC7Hc10ftthkWDhFPOu+CANaQMWT0P1MDjnT254/xpu7m\neDsWe3WqoToKUznhcbij3SWfAGF0GCrtrmSRC328Oke6nIIfIczFq5m5LkKD\ndUfkO4P1SnvCblLbrh+53f6Bt1xyeM8SaNf2lXF+bT3xakitq+U+KTvMzmSc\njU6uIVLyq6CVwchsyvOi0tEIoLAVRS8J4EPxDIv3xA0w9i5zKwaGjvddNJR8\nqgZw\r\n=iVlz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3VUIUUC16GItNuIu/vfmqhprvxtVdEIhCJ/OxJSHf0wIhAP9rUT2b0rLAQBTN/N6c0Yyyj47+0fK/EyJBjU9oEDxz"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.6_1623225334588_0.5673492784865082"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.4.7":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.4.7","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && npm run build:types && npm run build:js","build:types":"tsc -p ./tsconfig.build.json --rootDir lib --emitDeclarationOnly","build:js":"babel lib --out-dir dist --extensions \".ts\"","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@babel/cli":"7.13.14","@babel/core":"7.13.15","@babel/plugin-proposal-decorators":"7.13.15","@babel/runtime-corejs2":"7.13.10","@commitlint/cli":"12.1.1","@commitlint/config-conventional":"12.1.1","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/jest":"^26.0.20","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.22.0","@typescript-eslint/parser":"4.22.0","babel-eslint":"10.1.0","babel-loader":"8.2.2","babel-plugin-parameter-decorator":"1.0.16","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","husky":"6.0.0","jest":"26.6.3","lint-staged":"10.5.4","next":"10.1.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","semantic-release":"17.4.2","supertest":"^6.1.3","ts-jest":"26.5.5","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"gitHead":"a70d86196176c7de9521713bf2ba11a1279c0904","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.4.7","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-gYXCX+cbF7Fi6DFU6/wgdT45zL0h01q979s1ee6sBVJE4vwGJIdvm5HlPveEeHNb/O6WNY3l3+7AHG2oVdPkpg==","shasum":"cbc5498518906d780ddb9eba3da8583c185c6502","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.4.7.tgz","fileCount":82,"unpackedSize":182822,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgxxTICRA9TVsSAnZWagAAGgMP+QBKukNxZYa2p/5Xa6xB\n2xVhwn6YIfM71n0D0GsKWOxJTH8uzguPhiPXMUHcxMNQaLhgnsNw8cT7/A0E\nrJrjD4h0Dc6VwvFyz5AxB7vJ/mZYFtPxUiUR/sOWXkXBoAr+LTkuXuZh2Xpg\nT46RjnNUIcW1WtHRAuKms9Tjgdc3aVxINqAZfrJ90/gGb8Mo2PWUoh6pgzt5\nXxH/kPgADcpTaU7Fx/UbhVDHqGNB64Q5K1WOi91P/GO+YmyPbyu1llKMHZCl\nvcgNV3TjJFnANizV8FMzGxB4dgnXNtUyNVRo0FJ7RVisFb2G05SF6X8rsyuH\nUlh9fKQRfHXVAEerw0r2sNHKVCdihvILquL1NCdYAByp5MnYjWHU88DuECGZ\nY5H1ET2OYqI3TUgWmcAFKSiIoqNsFZHd+8bL6Ed5wzFISxJwuoCwJSId9FIB\nIfcjScMk7Tfge/ATPq+ceYPnNvR+pKfr5Ffsu501FAlgqvL61dw6T3juARGo\njo6ji/MVNBqcxNJWikgm3cbOjSfDjirPQvTcaCzD31Mu7vrPI4h9JPtX/E+h\ndE928iRckbEM+UJY2xTck5QFKhU//ym3u+JC6ZjUXPLcnZ3x3gz2XEbALbSS\nYZDTBghZShPxMjM3OwfNghiuiT0ihgyu0PGfVqenXD97qsEMcqMaZExbQbhO\n/fpG\r\n=mgEQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDs1EUtQky2MwM9yTfKOLUHdj2+fe06C8zbVeJsnqkuMQIgKbFrUNz6PeTqgLFV97iJuXkAnlcRLx6HYc2R5RM5JfU="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.4.7_1623659720238_0.1056014561960874"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.13":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.13","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"next":"10.2.3"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.26.1","@typescript-eslint/parser":"4.26.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"**/*.+(js|jsx|ts|tsx|mdx)":["eslint --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\nThis package contains a collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](#data-transfer-object)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nAdd the package to your project:\n\n```bash\n$ yarn add @storyofams/next-api-decorators\n```\n\nSince decorators are still in proposal state, you need to add the following plugins to your `devDependencies` in order to use them:\n\n```bash\n$ yarn add -D @babel/core babel-plugin-transform-typescript-metadata @babel/plugin-proposal-decorators babel-plugin-parameter-decorator\n```\n\nMake sure to add the following lines to the start of the `plugins` section in your babel configuration file:\n```json5\n{\n  \"plugins\": [\n    \"babel-plugin-transform-typescript-metadata\",\n    [\"@babel/plugin-proposal-decorators\", { \"legacy\": true }],\n    \"babel-plugin-parameter-decorator\",\n    // ... other plugins\n  ]\n}\n```\n\nYour `tsconfig.json` needs the following flags:\n\n```json5\n\"experimentalDecorators\": true\n```\n\n\n## Usage\n\n### Data transfer object\n\nIf you want to use `class-validator` to validate request body values and get them as DTOs, add it to your project by running:\n\n```bash\n$ yarn add class-validator class-transformer\n```\n\nThen you can define your DTOs like:\n\n```ts\n// pages/api/user.ts\nimport { createHandler, Post, HttpCode, Body, ValidationPipe } from '@storyofams/next-api-decorators';\nimport { IsNotEmpty, IsEmail } from 'class-validator';\n\nclass CreateUserDto {\n  @IsNotEmpty()\n  public name: string;\n\n  @IsEmail()\n  public email: string;\n}\n\nclass User {\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  public createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return User.create(body);\n  }\n}\n\nexport default createHandler(User);\n```\n\n### Route matching\n\nIt is possible to use Express.js style route matching within your handlers. To enable the functionality add the `path-to-regexp` package to your project by running:\n```bash\n$ yarn add path-to-regexp\n```\n\nThen you can define your routes in your handler like:\n```ts\n// pages/api/user/[[...params]].ts\nclass User {\n  @Get()\n  public list() {\n    return DB.findAllUsers();\n  }\n\n  @Get('/:id')\n  public details(@Param('id') id: string) {\n    return DB.findUserById(id);\n  }\n\n  @Get('/:userId/comments')\n  public comments(@Param('userId') userId: string) {\n    return DB.findUserComments(userId);\n  }\n\n  @Get('/:userId/comments/:commentId')\n  public commentDetails(@Param('userId') userId: string, @Param('commentId') commentId: string) {\n    return DB.findUserCommentById(userId, commentId);\n  }\n}\n```\n\n📖 File names are important for route matching. Read more at https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes\n\n💡 It is possible to use pipes with `@Param`. e.g: `@Param('userId', ParseNumberPipe) userId: number`\n\n⚠️ When `path-to-regexp` package is not installed and route matching is being used in handlers, the request will be handled by the method defined with the `/` path (keep in mind that using `@Get()` and `@Get('/')` do exactly the same thing).\n\n## Middlewares\n\n`next-api-decorators` is technically compatible with all Express.js middlewares. However, keep in mind that some middlewares may not be compatible with Next.js API routes. When using a 3rd party middleware in a Next.js API handler, it's advised to test the middleware thoroughly.\n\nWe provide the `@UseMiddleware` decorator to run a middleware **_before_** the handler. You can use the decorator either for a class or a class method\n\n### Applying a middleware\n\n```ts\nconst rateLimiter = rateLimit();\n\n@UseMiddleware(rateLimiter)\nclass ArticleHandler {\n  @Get()\n  public articles() {\n    return 'My articles';\n  }\n}\n```\n\n### Custom middleware decorators\n\nIn some cases, it may be beneficial to create a middleware decorator and use it throughout your app.\n\nWe provide the `createMiddlewareDecorator` function for you to create a decorator that fulfills your needs.\n\n```ts\nconst JwtAuthGuard =\n  createMiddlewareDecorator((req: NextApiRequest, res: NextApiResponse, next: NextFunction) {\n    if (!validateJwt(req)) {\n      throw new UnauthorizedException();\n      // or\n      return next(new UnauthorizedException());\n    }\n\n    next();\n  });\n\nclass SecureHandler {\n  @Get()\n  @JwtAuthGuard()\n  public securedData(): string {\n    return 'Secret data';\n  }\n}\n```\n\n💡 `NextFunction` type is exported from `@storyofams/next-api-decorators`.\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n\n## Built-in pipes\n\nPipes are being used to validate and transform incoming values. The pipes can be added to the `@Query`, `@Body` and `@Param` decorators like:\n\n```ts\n@Query('isActive', ParseBooleanPipe) isActive: boolean\n```\n\n⚠️ Beware that pipes throw when the value is `undefined` or invalid. Read about optional values [here](#handling-optional-values-in-conjunction-with-pipes)\n\n|                     | Description                                                                                                                         |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |\n| `ParseBooleanPipe`  | Validates and transforms `Boolean` strings. Allows `'true'` and `'false'`.                                                          |\n| `ParseDatePipe`     | Validates and transforms `Date` strings. Allows valid `ISO 8601` formatted date strings.                                            |\n| `ParseNumberPipe`   | Validates and transforms `Number` strings. Uses `parseFloat` under the hood.                                                        |\n| `ValidateEnumPipe`* | Validates string based on `Enum` values. Allows strings that are present in the enum.                                               |\n| `ValidationPipe`    | Validates the request body via `class-validator`. Works only when `class-validator` and `class-transformer` packages are installed. |\n| `DefaultValuePipe`* | Assigns a default value to the parameter when its value is `null` or `undefined`.                                                   |\n\n\\* Note that bare function usage has no effect for `ValidateEnumPipe` and `DefaultValuePipe`. In other words, always use  `@Query('step', DefaultValuePipe(1))` rather than `@Query('step', DefaultValuePipe)`.\n\n\n### Handling optional values in conjunction with pipes\n\nPipes are non-nullable by default. However, the following pipes allow options to be passed as an argument and have the `nullable` property in their options:\n\n- `ParseBooleanPipe`\n- `ParseDatePipe`\n- `ParseNumberPipe`\n- `ValidateEnumPipe`\n\nUsage:\n```ts\n@Query('isActive', ParseBooleanPipe({ nullable: true })) isActive?: boolean\n```\n\n## Exceptions\n\nThe following common exceptions are provided by this package.\n\n|                                | Status code | Default message           |\n| ------------------------------ | ----------- | ------------------------- |\n| `BadRequestException`          | `400`       | `'Bad Request'`           |\n| `UnauthorizedException`        | `401`       | `'Unauthorized'`          |\n| `NotFoundException`            | `404`       | `'Not Found'`             |\n| `PayloadTooLargeException`     | `413`       | `'Payload Too Large'`     |\n| `UnprocessableEntityException` | `422`       | `'Unprocessable Entity'`  |\n| `InternalServerErrorException` | `500`       | `'Internal Server Error'` |\n\n### Custom exceptions\n\nAny exception class that extends the base `HttpException` will be handled by the built-in error handler.\n\n```ts\nimport { HttpException } from '@storyofams/next-api-decorators';\n\nexport class ForbiddenException extends HttpException {\n  public constructor(message?: string = 'Forbidden') {\n    super(403, message);\n  }\n}\n```\n\nThen later in the app, we can use it in our route handler:\n\n```ts\nclass Events {\n  @Get()\n  public events() {\n    throw new ForbiddenException();\n  }\n}\n```\n","readmeFilename":"README.md","gitHead":"c2c9b6b197e1cd4ae431b55039aaad3ae4dad68a","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.13","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-0RDutGlhzxta0BOIM+EXVO4b3EZkyJUd67YxBdAtc54jl+tuVEpRLtWPYDLzeh5X9ukl4c7Iyim7xYE0aMD0Hg==","shasum":"0c04326b02d331b6ef67c770728d625bd94967b9","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.13.tgz","fileCount":92,"unpackedSize":85715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgxxusCRA9TVsSAnZWagAA6hsP/R4jSN5ioRLMf/anA8a2\n55LJwgX22oI9a0UEGX3OjcCP+iuoRPJAdQhSKnmhqprsuABsVxrCiqG29p4u\ngP7N9p+5T0bJ6BtLAR5wLjCU2kiz9YZl3jdwH3RsgZj2rYE6/JIwXjlOri7W\nOG2JiQVUq9uJ3MsPury+aL7HdXCDjrp3gsSV8QwkKCYmPFye6g+uskWRIrzD\nDgNp1/KEY8ncIhGuynTOGcVHIp/frXfCzZ8m5TPaD9IFlGsmucNJPdP+TCvP\nL35tP0IMS/SiZyvO4D+XxpdH1XHcKciVdh5KtBec+/iVkz+oLRdd0jR/PExV\ndaVVXC4PFcpQcJtJDW03PFTQ9jwiwUpaDV6m5XcDlVdC/neKEXyG62DgyoqC\n4OKjJhKMGNUriNZvF3PYd4xCspn1arfRHcM9rODjlPbuqZwr9sHJ7euMEjOw\nxvwUgw2BMoY3oQclabZM1UM50jX+m/AxKXZzRcK5rnrWU05R+S15QbwHPSzJ\nHUmS+Mla3Jguene2m8NW6KAAVM9FpV0DeW6jEKtJH+lHLHUu3ysIO+0Fly6Y\nHKPFQ3DNMtdV2zgYPqB5zA5972l7t+ABfrAs9Ldx9KYs+f/0SFP9JXJp/VK+\nGl+uwuJ1L29eykwDYjOkYH68871brtT5OXkp/usbzzPNjJ1arNB25sv/+pd4\non4E\r\n=dY/9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8rsEvf0rhHQh3mN2hZ9/ypugUPdC0CRwFw3lNRvvUnQIhAMmWXpqMud43YpGfpcOtVg8WQK5uzrSJrSl/77RMTdtr"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.13_1623661484482_0.7477888605559431"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0-beta.14":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0-beta.14","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.26.1","@typescript-eslint/parser":"4.26.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"39ed0e6d2189adfc43da799c00c7a82621701cb3","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0-beta.14","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-MEOKKy0MU+mqhCu634cs9g1/S1oPjJymNUckEUAzXv/u5k82VPzBiShzv7mVlAInm7WS7RfND1eZ6UAhW5M60g==","shasum":"86c64a7bf60cdfb54d07d69910e2a90cdc4012ff","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0-beta.14.tgz","fileCount":92,"unpackedSize":78785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgx3mMCRA9TVsSAnZWagAAYXIP/ReLypsaWx1T31L1XAmm\n+xTZzqqXP0GYsFhvX8F8/lghTw0AEPKj2I7ty11HGIF8PR1JLworICoqHXY9\nm/xWPbhGmn0r9uAep/H1Nab9dFLfTgVuIv2YM6cAjMirRW8yX5g4yj2MtVgB\nO1n9GBTBfhG/5P6B8/CtCCRvWsU3Vn55eunow/d0sNzru3Yzr41L+DmjoBY3\n9b2JMvRrCouRSY+QNgOQ2gmPw9r4VUN9T8ermOI4+hEwCbwyuywKkM1W44Op\na5Dd606DkQPSdl1DN9rpqZxwNZF+TyItoUtZ+C7o5e6ga3sii1fxqmeYMJTQ\n7WCuaOOWdodM6arKhRm9+kOnRWdB0smQsrSDMrD/xtBFOnlvNX+qfHpts8XA\nfAfmHAEIAzZvKwR8SsHwLjY6+Vk2jKZtTvdZeDZAFYarnrYmf2FgKO4beHu9\nLV7EBBXMyHjegnOp2DIag1b4C55l1N3vk2Ww1juBCzOX/tMQZaS/2QUKWmyz\nM55jI5uFXIb0UzgHvtbOpttGWHCk5FViApMRyEIKBWBDY/HcCzWcYQVo7YHh\nbaigLvn+tVrYKQgP14pjVWh+WR/VhJU3EnkkCwb3gMxHJQoEEQTEHcv1so5o\nzPlzE5XWiErNOT2jLR2vpgHErf+ER5ja2Rjatxl3WbJUMxdcwl4CRR4KTpZO\nfW8o\r\n=OitH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwDMQH+URUsHGZtEO09YuqeZQ35KaM+sncVkNrzE0jNwIgbwRGwmIe1n75jjWNqacFF8VDb4opcxT211vHV7o3mLA="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0-beta.14_1623685515740_0.8631657721430197"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.0":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.26.1","@typescript-eslint/parser":"4.26.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"8a5b74ee9b6f26bdd9ac9b4bb46f708f183daeee","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.0","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-sBvyN6yVLqemrwNyBpQZlBetVKrMlWDHBNI6JdYoUKJRwf/ZPZ+wY9FaloTSJWNcRU0P8VrhllKkm2o5x8nBDg==","shasum":"413ad510d967458fe420527a24b3b3741335644e","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.0.tgz","fileCount":92,"unpackedSize":80027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgx3wwCRA9TVsSAnZWagAAIn4P/33cCRp11LL1AnSUZ7b7\niX7o+zIQjY/NAWi1ksk1xrQSPF/0SH+yriOiqXCjqrjm/d1dPPlvIvvoe9ku\nUvbBtDu63Lsfi6NHv24+FISS3+TmjK9TImJ+H8uovtPeI0I8evPKgTDGOMan\nydzgfFQ/RTjFefZP0L65o5/9k9Mm0VNDxY+EFvnZJKKoA8tG5cB6FHbfvzNA\nzJw2fnYur0x+En0Ywx1mzqj9Pt5tujPfb8P4Yz6zzsJY1OOcyDfj2P0guLe0\nVKCBFGXzj4EAC2GRc2msztHJvZO9PH//owK4SOSjCbC9FqGcbm+umWwkcDZJ\nupGYWhOuL+krjZgBCbuC/Yb3AWPCPT8+YBRDfOvPWRs497bTCilBWwQqCg2S\nM9Fw6Jd0AHEliAVhONbPjTMXi273Lidu98kcP3oj64zX6swQ3iVaR9bvOGjx\nPUxnFqB7mMLkMYjNPL+ZH1z/xux18V+53gCu6qg/+LOrq2ge1I4/JihZyoVk\nIiTL6ybx/Au+KnAQIotbzTgr6HcPc8oHO4A113qTZzQgvaGkbd4dFylZdMCw\nx3outDjCUnEc0UQGLw2WZI/9kkwcAVeFu8H467IRI5ZLiqUz0vPKR1ix4ERx\nROOilUbPOft1Ea8j529lt5G/ZFtQe9bVebKkG6Sgy9ik630rmcq7EjulXl2Z\nBZ3A\r\n=8uvN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFPoIH8ECLoqEDGil9XuC/jVqJ6PhEkuTxt5eGceYDu0AiAv4qMryTwBc8pLEJH29OFwendINlCS3Bk3BgCSnFalFg=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.0_1623686192519_0.21260608311118911"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.26.1","@typescript-eslint/parser":"4.26.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"10.2.3","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"99814f5c2c044d270d3c9edfa3acafd0d46a88bf","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.1","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-h+/b1luD3PRpON/t8BI18aiyyZ/Us9DGh6Bfa+s+xhJFzyMf1ZzeleyBk1uT+H5oEPUx0iSDm8QfUL/PWtPRsg==","shasum":"d1e5b306f097efd0da2ac4f3ef34a4e62e2ac2cf","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.1.tgz","fileCount":92,"unpackedSize":78759,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgx4vYCRA9TVsSAnZWagAArbYP/R9sJzfknKduzaFwWVZb\n7N4kGqgMtESDMuYely3PfyGtDTk/nbv7+OhpXkw1/zTXx5c12UVTvKhcQIAa\nHggBlQRzM3aVMY5ttOq9W8kZqMTMtxzrQqHtavtN8VV4uFn1QCtvJKEwM4qv\nIGJQc8hhAR1ukW9oQtb36qUg9hXuF6HQSW6coF44TQ1KyLSprABvDapo3wGD\nDFb/Vb4jnRLR7QV0gSfzQBi3GJP2gzb8OEl2mO2CdyWjsJCiz9XOt/Kbzm6j\nX7MrL74gd5iYfgCRyTfeJhYTcOKU3+naLDuZpo4Sw84UuI8RRVsWgNMjp0Ad\nAiPfbchh8Hh0G4eMclBsG1o1HY/bKU841kiEa8PWcz1tglW7h2a6qaVAFp4e\nAc1pIONHhhWdfRLdx/H/lyOx3PnKaJKZAaaYBjh3tmbxowC8p+Guvytb03XY\nFm0RHOPfCTRBhKWamcs2Yza4vo3nZYsjK9KNeMoMfiRLrSpcMCa10rclHv6Y\n2ZDFcFkzCUUaGfLaPSwfY/5EwvjloKMh9pmLeght7doGicRRTlg/9yp8dnj8\nTNnTr4aOHn0+GbQjh/TCTg6wUR4SpXa1Ton9RCv00HHmMu+VvaXiTXLT4YBc\nhQ/T5PmlgoWGIRZDzTvdFHHzQQD2bRFqkkwPucx/4K9Lf7Qx+JIRVSq6GJFh\nvb3c\r\n=gS2R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGLKDqdUVzsNDLloy1kHVDlmW0hD/BpZ3bGLSpzAgDpyAiA/2G+o6nmGohrpO/VT5SmEcNM996xcy4icrpfcRxsMEg=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.1_1623690200108_0.5447873206107907"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.2-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.2-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"23231f68feb263925cedf7d614706a6f9805bb73","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.2-beta.1","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-25FQ5TsGZ8JKMCDkPi38ggW7ieVWCSX/suU5de11SfV69eepLmq0liq5e664xegYGoxuxsxfQnTmsb0uGA/oDg==","shasum":"c371c7dadc4f5bacd0c977b5947aac6deda8e129","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.2-beta.1.tgz","fileCount":96,"unpackedSize":80580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3FWWCRA9TVsSAnZWagAAhOwP/A+s/jeQ/jCXt+OpVxkp\nM64t89oNuVW3+WDbk6osMIN6N0PjrCK7WL6Zp1h9ttrnqij1LEPJQFE2nxTH\nd4yHWlwe6yNY5i5oe3Pe52xQ2B8C1Ul+rbJ/xG//OCPLq3Kkum6VBAReVLvr\nT+92yO6LvIfktU6Adh7dBtIT5eUBS2Otu5X4ynO3XMFDyKq++kJsDKdBGSo/\nQgYEp+83GZ6/2GFGfN2zzMxvOO5jKPxODropZgscqE5ihYbSGapYBSAPbW3R\nAOxAaMBlR2uWDdGMmCLAeRVBB2XRRiq8atk0G9Gcd/PDhWj4FS9jUjgHAugF\nlX6M3/7EyhK+SzxHyyfAZPj9JCntJcdGRTHe8oB98ReR5SL1QdzJc4dzz3f4\nNwbxyWNDfaVU61rE7mi8YWI30vgo66zrYl+NlBKg/ja7/oLmCoz4lhJ8+fF8\n6Uhr7sFDBeIXUBMsGdIyfSSM2ns+VYBwLoICUu5y2o/E0LJvYoRlmw7h9G0L\nokfCT3wEz5D93uH2D7hKxEA4K4E+jjASjkGI1DtLYMfksgJsi61VrFjpgKuk\nXrs/FzR0La9Xr0QCkPUwKf2168HK4hnZCgiIPaHYZmo1CUkMxY0KTP5Lv9fk\nASPCXqM/cNkNSUCafr0OfmNSj/GYBSaVzyilX3gTt5Z0pwvgnxBJOlgnjfSg\nAQjS\r\n=Gson\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGKpOTVPgMWa8lkzDCOoTerfcFCM/1HKgNLCneNUzzG/AiEA4MfMGAl0/hfcdt0/tDH6/r3BUm1gHFyH1MOz7iCNqx0="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.2-beta.1_1625052565584_0.4923109809324313"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.2-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.2-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"c80f2bba1f911ec58018e346d1f06447bcc87fa8","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.2-beta.2","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-jV5EAuFrRHo8IX4VpHjwnhWjhPFHR0nyp9/XuegiRFhheL3N3VrkgymfWamFx5h1mR/9nBdNQIfQHxHi7E8fUw==","shasum":"b67af80a3b023b2ddad65fb2b2c156cd54d211a2","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.2-beta.2.tgz","fileCount":96,"unpackedSize":80414,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3FigCRA9TVsSAnZWagAAoeoP/2geCFrnjzmfwUDAk6pN\ngeHNEl7luFeca8MAaZIAfIRxfY6ZfnaL7D7irP8xHm747mKwNQ+zHM68UaZD\nxD+9ss8LxW1X4kMXpdYJDwmbinm1d4MepFrrtbCEG9JNTty5QJe3PJ6ZgxrN\nzobqJU+AaM3/0W9zhDG3FPq8zc6NsCt1EU3ZW+ail5GowpcesGpZrQa5PY9L\nVp5bBE2080N3f5KAD0tyhfQA2iwZ43fC9/U9eRvs/jUQZHJebz+3Dcp7fDWF\nnp0nWQSkdZXlwqoLVY5JHKZ3Nu+vNcwu/naF7z+bVSPj/utK4Of+emgR1STw\n/O555mI4BgKlMI1Lpe8l0nzEuyrcuCo/BQ40tSl4luIR4kV18P8fs4AxO4Dd\n+rd5cqro41VsXFdtChnO3DCcvdWmhOlu74mjn2qmNmv5egI7rwWT47C7TJ5V\n9SFRZV0C+mQOJP8VXpqV0xiiOzeRGLHpka7+YY5qsqCD16QbRnQ5bQWu2Afm\nQYnQnkxzXDDzzprq5XLEVS9GaFVsVm9YoEMsF9HK8CWd5IfUTRuGD259seGP\nzqxUxaNHB0ZmIMO3ll+iu3kgQyW1zuw4FlcagFFidDPfQ0X3x2CY+PpIgX5Z\nb3/N6dd+jSR83LfVuZ/uZRZb4gyu4VrshbGZ6N2iH8phmSDI5aFjJX+gzsGe\n7ml5\r\n=xnKV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/8Jsn0cOT9wsI6onkfrVZFmXWewBavLnEub79gSu2FAIgBIhuG3nZxPFZunTL2+1ySR/eOJes7X45Hg44UBeo89o="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.2-beta.2_1625053343537_0.5974912837984958"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":"^10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"c8d20ba0f943374ab747d92f3cd0592beaddf8b9","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.2","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-qBUNbMyNORLCvNQLxHl67e+mMv78fFQ1ic/SZKJxnfgWSRh+t6JOHgvLzKymr6Y312q+7rEEqNS9VGo+GXmywA==","shasum":"a739cc9d8686f5d2cceece7cba4a30259ff8f196","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.2.tgz","fileCount":96,"unpackedSize":80559,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3FlrCRA9TVsSAnZWagAAPhoP+gObcFG2aFtxvgtkFSxh\nndqFYNGTDD6DUZMxKaed1XvxpQiVKy65V2pdivcs5dxXyP4iY4GRvYKTzpQQ\n2iQYXLHjftjgzIQGIQ2rRoUHC4HVA+oZRJVvUdfvtO6A6T3SVyeek8xc2ZKa\ncLfhKl2uNohSwTRb+vywN3Px95yXvF2MIvhddd3tiuo817LSwDdoFYzMumTe\nH5urO9aCn7gKgW37R9h0sANX/MUWEJqoyjAI4haD+Z2y+Yn2vSmPs8s1DoKp\ntZX1r6Ph11PHeI+T40HGEGQwrzW3nau3ljN29H2ZTWQjwQbc4m3SpnDuwBk5\ngNeD0nNln6a2GS8Bsq7SKUtOw/MVKZZrHgNRyEk03SsP1WALwWoMbhkIRRdm\nZUv2yevZQXFvFm0UXbFZwkSz+HP8U2tW3PODP4+fQ0oBi43bJGWy8wWKfoE6\ng3FdB9v1iapv/9ElApD+WqRs24lHpPPNJ4BZ3b7hQA7yB5W7yurnnFvjRQS7\noYYb9zHFqwC16vEvliW20KvniSKjemJa5XwwrX8Y4pDxUpSFysNrg2MYCwZO\nL4+Vl/+ssM319tK3iEFciUAnRBTxVtXr02laXJD6qItkQzB3zBueJ/q6nzQN\nhTtVEclj+UdyBUupQl3Q/2MBfV8gkivP2DiU1M9xrfWqrUAKWbdvR/oOKb4h\nGaRS\r\n=Boj3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQMTHD+8sqaqGfhVlmxt7TzhBPRj0YeiB/mzE2cgxqFAIhAL5ZMieAL5O3d8nZQekZ4SkbwS+H3JflNBJ99gSQrjXW"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.2_1625053546916_0.44247597980689357"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"29c2a4159e8daf8e3da6d754c2c63d2789be25e0","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.3","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-tUvUShPG9urrN8r1Yo9P37BQ22T3uxATHd09xG3GpHNMwBV2nQlOgYl1W43qfCEEMIIAtD9yMF/pThao5XP8fA==","shasum":"27ea155fd7586b62b4187d3343f892e7c60f48de","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.3.tgz","fileCount":96,"unpackedSize":80539,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3XwWCRA9TVsSAnZWagAA9XUP/ixgdXNN0sOzKM3E0Glk\n4ihZs/axCsOZve40g+YQO5MUYADYJHoMVaSzMyKSdwalJDpqkJV4J3fbGNDR\ngXzZ/Xz6UQR1j3VLDq2JQM/TpvkFgOH1wvPJYv0QLqW9SxZX8zyUDBXZhQCc\n30xLR5jHt9AoegMIUSEKpN+Y0/5YXx2AKQALaNf0OV6TG7K+4aLHc5GHA66r\nXLEJFgeDfufynXbt1Pe2u+V7bGfXIUvU+dFaLdTysMM6GM6t3JbLI1ag3jAS\nfO1P3/ctof1Bz9zBtYYNSgMbdDkqlrIe7y8upB/4jFjz9NgmjQvEEDdOUExL\n9t/UEksGVr/lllpj3SYVeeh/qXaenOLjyf/15KSDMMf7MrSARdPobleO4EMm\nNgPZf5rCgw8FXYoF5MbPXybISztZY4cr/R6gsrvnKpXreQmbwjGwsA21+VeL\naHDFdOnBIgOtNtE9LqZdRMgjIobEsfiWXDxh73oL9Cp92Pzjgo+e/vLtm6Hf\nj+a4tGgqQ7UvlAlCrCXwoUT8a8g2+TRzPn35fZaXN4Ziae/ipAMn42I/zhvq\n/mq3xR6+t9zgCOYZ8n5p0LOl16EMca8wgLrck9NWkN/eazLcSmGfjt/S4Dqn\nllK2Or9wEqnKD3v/kNcRhwZDR/ws1mHLt+ZjCsYPVGxPn8C8Aa8jbsLZiMBZ\nIXnw\r\n=WESE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEAaFrSWyyHp8I0DWj0E1+n0DoXGALLOwLEoiJrGRGh9AiBgajgTldNz7jGzYOJseZwzdKY18N5C8gDmol6PcwgaMA=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.3_1625127958242_0.0653351822322572"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.4-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.4-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"ce4b67d0063b27c3bded934d2ebad0a4a4f72527","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.4-beta.1","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-ezKEd3I7SZiT85yBoGsWIlSYoGjdg4qaXgmVAuhj4PVuRPvKt8I2h0k9WmD3STsAA8MCuF7zldAyrNjr9MNiQQ==","shasum":"c821cdcd4b2a2739f0afd0d6649d3f83fc4e3e39","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.4-beta.1.tgz","fileCount":96,"unpackedSize":81743,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3xUZCRA9TVsSAnZWagAAkdAP/2v5sliioMr3cNoN3K8u\n0SIxDAJuC619gaH6be92bh1vbBtkTxqU3GW6uSK0nkHtvwrE5xCrIkVicyCk\njYx1DsA8idWHHhnQ9MCMQg62n7qN1mne9lAoa1FO8df0pmhilMny7b9HscYM\nExTCDPXSswfPVitVEKhkabO70bsdoOCOq9/QMauVW100WQ3Svk59mXwwmXpq\n5Z2LtyDff0LTIV9ySvSrLC6TeyQ+fBH9dtvZGWWWy8B9KT17NWPe1LM5rEc2\nbfivBOG+bs0dl0w6nqcefemkiPOZA3Jb2sO///biA1agTRNuvRUe1JbZ7DVu\nqGL0k56PSMuQv8KzJ41w7xKH3yxuhIe8NPOK7TgNvmAeDVpQKLgTdJWc9g9K\nMK470fgIxqlvm9A/Pnqd/W6nLDeGDQW3p/S5k1KKFhL1LYtYZY8BC00DAgwD\nlQraXokPn54eWpox7ZmZZesjv55QA7+SWjUYhSQLRofEJryVxq6Y4yg8HgMV\n2xsNR3K06683T5UoBm7eyCCzSXSSoT0SQ18MOTO9BSejTQsNnObvY8h38A6n\nPVRovmYCmYFsCRepf1MSSQp76NQsZuI8I5qImSSXWpscqykJh5WXPgfF7Tom\nBvKaXM6OjOiBGeW30yE0VuAcOVe71NFL59vbpgLX3ggPjW951A1pZWBKSk64\nO69g\r\n=yx4P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDX9urHs/fhkrOwqcyUrZbyxbRve/y8wDm0pqGTiYz03AIgBz8sFKGftjD/lIX36AGtMuNUFCdwYh+DQDQ6wo7xp5E="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.4-beta.1_1625232664656_0.9366383910620628"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.4":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.4","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"babb038b1970ed34e86647076dc52154d038220d","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.4","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-eCVhaJ3mp0UfhzllQGSjNRGIVbx19T+POCgkCkmmsS6PyRvORNa9ZDDuwbUE3afvoSzdDfWojqfS/kLmJ72xEw==","shasum":"16b321f940dea63f739e30e0f298e8af3b896c6e","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.4.tgz","fileCount":96,"unpackedSize":81722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg3xYoCRA9TVsSAnZWagAAYKEQAIlrhxu0UqqKFdd1O3F2\nJqqSZQ9XTnh935nXfqM7PFkaOXV6OUcGSlzqJt4HI3fo94xaa7tb4HLy0ssw\nsgRluSzf6Z2uEk0swEdDgUF4DGMg+Kx2oLc37grSG8cd73VI2/+fqi9INsaX\njcxov1QJXGiOyG/RKnvi4ePzZc82x9N/hc813cgvnbjyc4qAAt0LKH7ouGtI\nIM1HECHl7K39fVCyeJ0BczqLGja3ZfXCbFIOclXpIHLMn3Sd+sRQlo5eBIo5\nGxAHF0Ht30IIhjHomFX7SS0zuYA0K45oyqV2QHbhLX86Hmzjt8gVxTHImYk5\nVZ/KROGIh1IXISGtnX6ImTgG0nyoi0IUImi/gUESjBX4VtkpBKnAmevlKldx\nT9ENskIdsnLcWk4ZEnSd7Xbcwg90OcioUwPIs4Ui+hYUZnjYD5peZo0XLou0\n/9Pejv6Gp0BgSVlXV4EmA1ISZEICUKWbHUp6OBrnvbNu+vGgDAaICjYqRGWw\nKZqma0s25CDDPICaukNc1AFrWzlDe8TsBbzCizwsKRNPS0LR60IUuBfNTMSO\nC0Fhu0dVcU/gOtJOx7WTxJgwkdQrXaXObbwf6NscI+FcqXGFg/1o+/Jbn753\nRqJFI7X17oGBPVLllbnbBRWpNu7bmyvSKzX87S1+EX1ezQbURh+o2ikWhNG6\nmGzt\r\n=cF4E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCaS5ZyLnfp/+rlCiuOT6nyn5KTBvCu00Zd5qPFiXlZhQIgDIb63eqmd8CNhgGZVELtkvdlkdhO3MlqtB3meqiE/D0="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.4_1625232935678_0.4726227210542937"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.5-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.5-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"3c4d5623c8b27698f199dee621092286419b6702","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.5-beta.1","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-F1ctYHK4QbeTWvjkcfnTU1P6gYF4eXcQhx9ivKkQJMBm5Ev2w+QnmGlQaPKdrdrI7voyuWoHPrVZYhBd93yFOg==","shasum":"dfabda243e9067f574883f44e647f0ea22411079","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.5-beta.1.tgz","fileCount":96,"unpackedSize":81577,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg4DYmCRA9TVsSAnZWagAA/GgP/11LAObAIetsGGUQvcZb\nMIzfzpHuxGeL/DX8D/IBw4mA8wolC1iKLMyxJJIe7x9ZKa9Zl0J6Te+Y33yW\nYHthjGoeGh37NrPnDZjfkYSLEP3fbCs36Ll0g7WUEAIQwAQlkaav94I4w7mI\nMyQrff1BYb4fXsMsxADBaCNH+idQtAT2tyY0ag7P8IFKgQ2yFouKXxpp/cAG\n+zf77cuVow0FJ0F78AbFJ+/SGWVo48Q+tNoJ4Znk05oPmog9xv6ZDiVFPnc/\nF0Pjxwa+G4U1NTvJPyA1z6hfKjY5aZWH+QhH4zhHOpeqMK8LOnvtwDyxXBpr\n0lrIKVe2b/1PC0GbfTS2JlB7kQK3yCDqnQhMbPBkk5ihvCknS/lMXiJpgkPA\nnxkM3IVzK8waOOZUYubY9DxVhfjzyLCnV7xM+rJVQB211tOZIMne352bMAYP\n/nx8n2kDTbNAO7fE0rvgIm4kMQUFdZJBlYKQIRwzG/CIigVOuoZk+YV/HBiB\nkLMNwU94eON27UfuW38RJjoR6PQY/BUJEyiP+OGECUEgrRg/7SFY1bUIAVD4\nfjiRRL+9ia/CyOTbkv94f6KIQon0L5InxvGRFGM5LJtYsURhVLtbOYDjgcCc\n27wLul0wtaLQ0tV1KwsnvHNe990RrOUpziJjRoq6vUckGpYlWW22fNdnx899\n8g/2\r\n=HsKZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGIVv+Ztjqu3uJZm6Hqpl9i9kU99TwNu5vD1lhV70jrXAiEAlQ+tLoNHn5ClHDzF3Gx5vevmIjA9k791+VYGPlm9uTg="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.5-beta.1_1625306661989_0.09784512029243841"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.5":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.5","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.0","@typescript-eslint/parser":"4.28.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"6.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"5f56ba380152458a7053c28bf982a46ef39e01f3","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.5","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-YbdJ5KF/bQJ4UCT+lvZN9186Wiq9X7DQ+8P2rjT13HrEoVMxhNoynxztvCdKS+4Ypdx2H9jtil3qSpbXaMEWNQ==","shasum":"9ad7001caa029b09c1528535fd303be84735c9ce","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.5.tgz","fileCount":96,"unpackedSize":81556,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg4DZ2CRA9TVsSAnZWagAAq/8P/3DBGvLXvPwUbHj0LXmw\nBH1Q0af7lD/3OTcoKcXD568tyBOH7cKQipl05pwsiBRz8BBDG+nN9ZRbdI5F\nHNtuEn0KQ7hx/EjN6rt2pRDd3NtmjXeZ6sP1njwqkv1Fiac9lty8nW4VlrpQ\nuR5lxqxllkRiNV2aHRvUuHnnSwd8DjAZ0dPvlvw9kpKrB3DA0fNkfB0LUY8c\nvHDQ6bo4Vapk8ohEL/YVmjz9t7WT//9bO31U60yjom1IRw5uq1b4Zx+WihbW\nSx1Y5DwyzDJkbpXZIhn663A4MfwRt4h1YTh05cLd26zNJE5ucyGwzvy3Brku\n6U5E9kN0pQzA32x9/YMztwJjzrhsgyHcdlR/CR8BOhbqLzT4iY6OPXiy5Teq\nWZe0VYcTCeH3UPGjLBkO7GJeHZ5g/uBDzy3gpS2z+stCl+ys+nKMK47yjqG6\nfZpbsGsdrAKmAMv7/fORxNxOkWm9FlkR5xsnKPBCv0uSrXuvla+FuGwF83R2\np5WBeY+glY5rYPDrfik+C3ZWJI6JXdC3FJFBGI6VEBB0cWgw1yRR559W7MQ+\nVBcbdAhi5k2rTADSRcGjlwpNM3BYqkY6ma4Kt+8jESQik61PF4t+K6nCyP21\nixOF+OACwhn3iQKXq6jMeN0A8NRU9APP8Fk0YLSk6N8YUm6fYrBZN0cAE7Fv\n51cW\r\n=MZV4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwQ92MgSCmO+JD5FGoMSWlrhEtf1+5+rMc5xOu9YkjOAIhAOWUA/58J4PssjOCdIokNStyQWwiYx22ITSL2L3Sp+ZS"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.5_1625306741554_0.11641462649806011"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.5-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.5-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"12.1.4","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.1","@typescript-eslint/parser":"4.28.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.0","jest":"26.6.3","lint-staged":"11.0.0","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"5f3d06f359d3cfa221df323417c6fdc608a711e8","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.5-beta.2","_nodeVersion":"12.22.1","_npmVersion":"7.9.0","dist":{"integrity":"sha512-6/GFH3RNZg0YhKz/hslAJ2kszRQVqzy6ttjs7wFPhLy5C24ZuoIDsgNpMLIGd1+UeOpOY0+R4bhuKTX3xmD4HA==","shasum":"33b6edbce0a10110383ce33f5f2c06471d9ce1ab","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.5-beta.2.tgz","fileCount":96,"unpackedSize":81425,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg4rdcCRA9TVsSAnZWagAAgYMP/iWGOu2BJdltxz8m0lGz\nzYOrgQPyy80oq7zbkQg6rgtVE2fBPRrhDuMWqq6MRCCLVfySQLVMDawVgJdW\nJaivgNP7ZdwY2Uv/19GWTqcH54p3by3ijmzN3Ee7m5GAwkbbkDugE3BAUJsd\nUWJu5kCN2BUYD62KQoPTOLlKcabpXzZhq9uwXk1GnYecO3Ncu8tCRTSeeWyS\npo7ml8YFLa/TyExuyqOaJK3xaSoessF2RtkRss5OV6De7UnEhAAu4SjfMmNC\njNpNiXDUTFIGqQhP/9RLt57VQWzagtf2XPR++2CVyrIRcKGL/KS/bmicP/n7\n23rg3kTT3UfUzoXvA9YlA3ZYBAGL3yZfpblKj8a6w5zokzkfhgx5p7PH4a81\n/zGrTwqxR5pTRSYjV7IaHd/qEufsCUjapj81r3TRjBsnKiKAXaHPrh2zrV/C\nL+oRYHOIRukXP9LUgBX3d4/c3HGFmIzjv8UuaxMTvPB//5jiwDRob+i16Gdx\nBHzvZjF066hkN7ekcBH+Tf4Kw8Sm188AfO5MCBshRBK4Enf5MYCXZ/W60kjG\nQ9t6wAteFAQXODPihtPAmtcAYmWDJlZcMScy6p5y1oORftZSDYAEwtLIwYWD\n3zq6bFfehlZnmIgisvNXXXAsQ7WXecPUfzolaLTOgLZ8PXMBjMKNiJ5UDVqM\nUScx\r\n=bdjR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCH1rawVBDKUkY1+nXAKGVp1/jCzeo59lXITpKIT8kLq8CIQDyS+RFuOq/1+SRnesfJRFboDktmVsMMBojCpoVAOOC2A=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.5-beta.2_1625470812095_0.239697497850905"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.5-beta.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.5-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.4","@typescript-eslint/parser":"4.28.4","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.1","jest":"26.6.3","lint-staged":"11.1.1","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"7a3f70692d1b0a7615799fda0c07ccdc730ac46c","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.5-beta.3","_nodeVersion":"12.22.3","_npmVersion":"7.9.0","dist":{"integrity":"sha512-m2pn4/2LNx8Ilr3Iz/iKBthhSiKaLwybY5qV7ZXtb1mWiOGUUqxFPeCEbl8sIHzJOtmHi+2XE/yzFTEWGZFUNQ==","shasum":"6a4b5237266c17874db7d411eb8b4c36e52b5ae0","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.5-beta.3.tgz","fileCount":96,"unpackedSize":81616,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/u6cCRA9TVsSAnZWagAAV/EP/2I1cuim5c4Cp5GEUfhb\n6eo80vW4LkqH2Nm3YULmkQi+wWmMGAgP/NdbebPpnEPGUZXggXTBWp1WGvIA\nkv1AqB7ZfIkhZHze0trVjDOAhmg34pGKvpkcmsun1HFnb0ll1SGBwgqG5PUs\n4FGnN9JDVlu+FztnadulAfgOq63QvnXrBxiBWgkyp1Gw42ie+PRfx68BoPwq\np7RCmo6qB8KdlfQBegVXlf2q4AFw6D624ew7sfmx5JFGrMBzWYCxOdAGo7bX\nPIaPJrckrxKOXKMxNjdk2p/aCuhGSAO0yRyfItlPP4wUfvEkyoHXoByqKcp0\nKnNd5IuEb4N3rEbzQ66q7HgO1fULpkp9ylEcJglEZGd8RNf/x2wvGpO8qmTS\n6g7lCFpfIPzYvz7ksFe/JihFClw8GL11zAf4AV+zGm1sK+1dCMgeT6+aUegg\n98cheIBO1+ggvODUMsjYSVN+j+6zzb1gIMQJwFNz3nco1W9NBa3hed/0wBwS\nHJ1dflzYikp9GhBRji7R6XKZkwGI/jW/1wFdSsYpmNWm8jjWyr+Yw2TKx90A\nNVYPO1jLDHDL3ETVGdS5pQZ0j+PthwGCkpE5aHnS+FgUfa5QQRiBmlgESaLf\nrcoKDnnV7638oX1dFw4zUa73QG3UHUePJZo7rUs4zcp0zEQgMxOR29pJxF+j\nt/Yw\r\n=9MC/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPnykvlZidujxa3JFe8H6wJ6hIgtGaiDEXZzw5Ka2ZhQIhAKvaVLeMwMtGZHfYkPqu1gvSLnfpmidjS8BMQchJAZJo"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.5-beta.3_1627319964220_0.40707962604557846"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.6-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.6-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.4","@typescript-eslint/parser":"4.28.4","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.1","jest":"26.6.3","lint-staged":"11.1.1","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"ae424ea102b199479e5d1f474ce0a7e335660d52","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.6-beta.1","_nodeVersion":"12.22.3","_npmVersion":"7.9.0","dist":{"integrity":"sha512-Rib2d/swLM7Y8CaMF/dl8R0aJYh7uxaUbkhiG3ziDt4OD4TGISxMA2Tj2qCzN8DblpUWKP5v9djXnsLGuqmB3A==","shasum":"273ab2053690db75470ad033db3a80998ecc078a","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.6-beta.1.tgz","fileCount":96,"unpackedSize":81609,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/u9xCRA9TVsSAnZWagAAr9MP/jzOeLLXxNR+lm5Pignc\n6SBqcl+NlcyRZR6Dqn/jWe3pvzcfm7p5PjKHlhx1z9ItZYqMmncC19Z7g5S2\n3s5Sx624iG4esH0cnDgSIkJuaUFVQcgM6stGHQkFiXIsJAeCeCwHHICUzegO\niSkOn86PgVh5Zgolc1SQEKXwFmeTYANDuPXlC+BZaKXJxhlX0TGnPxfRHpY8\nnmhfbIVQ34XIUAnov8Snxkp5+Z1NSD5wJvc6k3dwu9Wi3neTRAy8aiLtV4q4\nTPwWL24PAIJ9lUzYDc4XFB6apTD46501aZwKMmG+zLBZcNtx5azzPt6Gt05L\n75N97+DULbsQuhlqijNazAekP0AZLJHzr9jORnvr3ZPk3K8c641AKttT+LFH\nsB9i71knBl6aVJVQv3UThYXFACYNAUfcSnuGksG1g+26/B+dPZDr7DV42sbQ\nKAlJPoeulOvoCURaqylNk8dk7ByeXiohlEeQvaLkZk4aZ4qka4b6H8UW2W7v\nC24VzIeUIE41qWrD4Wz+mYJizU6AyhA169uFvpGMNpGiiM/c4qaIeMu/f/Xr\nq+tn6C8EZhzlY7Egfjh7dk3OlZmFGdcOwOYsQHErPySxL4KtvF6jxkoCeEUK\nFKxqAnR+tT6DRnisMPhK4fHRwIgFImHvepuMPo2HNDkjNs4zRjTIXh/NrX9K\nPOtL\r\n=s8h0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFakI6R8Ct2jHZXLO+rX4oJ99XyvgFUrP6FaDcZyy104AiB7+iXZ6O9QcVAkDql+A6sos5GfMDFc7yAPnBM0jX86cA=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.6-beta.1_1627320177039_0.002235513650930221"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.6":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.6","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.4","@typescript-eslint/parser":"4.28.4","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.1","jest":"26.6.3","lint-staged":"11.1.1","multer":"^1.4.2","next":"11.0.1","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"d5c77851f5a130d68e22cdfe8964b309dedc04cb","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.6","_nodeVersion":"12.22.3","_npmVersion":"7.9.0","dist":{"integrity":"sha512-P0QYnq2qWSCmCqZhCfSlxhc9Wr5Jbtxvq97u82i3GwFoBYxGFtdA9JQJ+BTIbB2S+ta9USbXur82iM/qd3h8Xw==","shasum":"31cb2d0efdc6e77e49491793e2401575661c1dd0","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.6.tgz","fileCount":96,"unpackedSize":81588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/vEYCRA9TVsSAnZWagAAZ40P/0LYIDByENTRPAhGj7ZS\nS9SFNhdp+g9ikgydGFwOj6wFsl9YeCsf6WnBfSevhN57jJI9PTwjnRr5/dI8\n2qzeDJ1sDsHYAROgG51sXNRki5GNpd5RprjFwq78Qnfra5vM9E7elWDtqejd\nEfsDrZVphwzURocCpVNq/t7nK86PVEzavb6WxvLdJ+xjrQrGQxXXAXmMmlzK\n7TFDZPd4mjIjHRDY2dn3nOULW0o5uROSEFVBX6gfV2AWVrIrz5v6Zxqzpvch\nJYl0OS4+9U2HwbE7EQ2m8kGK6nNl2x1otIISQLdqb/HCI+CuYC+cR1b/u+0R\nLYy8UEQ2By05YQ2f7/Tv0vnIbzAEkQbW4zIBNXmd4Ga7pmsqYsCHShiX5tDb\nd+CmLMpQwUjzuc2YadWP3zJ08eAfp+wIXYgYOsZqvtVA45vyHWBTr2x4OKs0\nJ5kAel6OezRzelCw+vovPEpuH4O1HT6NwJYPzySbi2U8c8xJbVPyNR1xC2tC\nFnAQd2Jow/yKZtUtV99lGiHWIPoRzJp46Ihb7+gOX0vozvd1radqgX/vLllM\ninBNpazv0ou5AzpFTRD6FPhbK8SJnpYnqh8jaAN7R4zY8jpzE+mvoP9CeCAr\nucAREyJ4m5sjoRUhk4uWEzjeqwqGimES54tE6dTXqVlZb2PTlovvr0Il7X+x\n8MOY\r\n=V4UB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICB6dzXC6eBAxLQ9TEOq7TyrC4QumAhYBynFQaw9eLhUAiEAtsSr0KoTE5S2gHlG3+NkvwHLpUFqCEjpYXOxVCNgozY="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.6_1627320600575_0.2873634317788418"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.7":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.7","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"12.1.4","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^26.0.20","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.28.4","@typescript-eslint/parser":"4.28.4","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.1","jest":"26.6.3","lint-staged":"11.1.1","multer":"^1.4.2","next":"11.1.0","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","response-time":"^2.3.2","semantic-release":"17.4.4","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"65ef6f2f83a467650a0dd12bf3683ca202729313","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.7","_nodeVersion":"12.22.5","_npmVersion":"7.9.0","dist":{"integrity":"sha512-kmvuSuRZrRN1Qi8j4NnlLhyKllolUd9dfQP5FgcbLB4z5l1OHC03XnimWXZS//HjyVn/xMLDu7NlqwCX0lJ6jA==","shasum":"4c441b7287ea327238dd638f347751fb7302e1db","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.7.tgz","fileCount":96,"unpackedSize":81718,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhKlByCRA9TVsSAnZWagAAZU8P/2MISxgWGi5YneKrmDb2\nj3Lr7bhgXpgsdUdZYfGG43cgMARfBqQLQ2gaDqxhRePP+W+5CeydsDfjMh6s\nOBM+aK+4OO32TCqloK8TS+rhfcaL7RTujNqUn9IKMGCt24LynLuSxjLjE0cv\nzYkobfgYVyKhb9dfqf9feqRAWywF0L3zq/saeMGVRP/Der6zBm2EeBwBBJob\nuGcisq4pnGYiWEOAv8OORYKiGwDXwsls7FaVuzxAqQ8e33mN2BbW0pIpdn0Z\n1Y3JiRmpSpDqMrEXB+4+2AIA5clzFBi98Ar6M6bdtOpZdibvR1+UEF+TU44R\n2wp4g3aQ2uyuYH42MZlPD70iob460yGsNjFMjCXGV9oUjrlwQGD0/sU/+yD4\nz9KqQ4hCITKH/FYin9RMoLq7jYndmv1PXIJNLDY3pg2ih+ddyKHDw6Wb+Joc\nyZqJXWpepagTYPYY5V1u7iFxexJswTBVTL6jTGGxPCKMZeB6nUS0b3Apa5no\n/HKVdOog69uzGfs3KXl90dwx8nZT2+4Y6mhkTcfGABk7UBxnFaA3RwM8/vyp\nofcnvjM3j+fSDYPnbQ66PlS2/q02VjNfjskkiffqOH53dl5PKwspflzY9D5w\nq53y4YXaELQYVSTwy79NoaFUyCISXcm0fH0145Agz7EnyBGR1zeZxGOn8r/n\nKTya\r\n=VVUX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+PlCh9k1Rf3H/Josa2Coehd7HblkV8VkP1wLCf6CjCwIgN9cjyN0dMD6yQ5PJAonbjTx6tMVSYq/OzDs6xa39qR0="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.7_1630163058510_0.6220185312097786"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.8-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.8-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"13.1.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.31.0","@typescript-eslint/parser":"4.31.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"17.4.7","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"19b0a90ed42e517921f76cb692b798aae499d818","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.8-beta.1","_nodeVersion":"12.22.6","_npmVersion":"7.9.0","dist":{"integrity":"sha512-AZaLpPHtHT2yvJEf865zeylsRE7bC/v86hZGjYIa8jnNq/wPaEUj/+uwoyGeaQ1amvzJOtHUCjr85X90PsRuEA==","shasum":"c2e8898f9df9215dc037f1625448764d2f2609ac","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.8-beta.1.tgz","fileCount":96,"unpackedSize":81752,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXkgt7p6tyZwJ9Tgrt2iczRBkvtX0qC7aI2O1iDFLMQwIhAPOyB5GKkzKa81vFM7fTYh8wonH5qvSFCDbqbm6E2USN"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.8-beta.1_1631876631622_0.12339984877964194"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.8":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.8","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"13.1.0","@semantic-release/changelog":"5.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.31.0","@typescript-eslint/parser":"4.31.1","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"17.4.7","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"66810f14490a9cdc9ce02c4a90cca1406de44fb3","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.8","_nodeVersion":"12.22.6","_npmVersion":"7.9.0","dist":{"integrity":"sha512-T3+induOQ7fM+RLBEvr5O+5mqYM65larGF5nPMSkk3qy+nORVlszRVqe4hUVyVaafSTMDc0G4PjZAbNzO1XB3Q==","shasum":"5908aedfc6333e63b45103cf224bbafd50c3c507","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.8.tgz","fileCount":96,"unpackedSize":81731,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBCIzTVO3/y98tKBFaYgGcmRLZKr8xw1RZr7vHBIUBFBAiBUxKJBV/Jjpf/LTNl52/YT+M0bpAIWI159TXMUEe/qrQ=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.8_1631877015701_0.03536883823050796"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.9-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.9-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"13.1.0","@semantic-release/changelog":"6.0.0","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.32.0","@typescript-eslint/parser":"4.32.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.0","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"8d8f9beaa90c93a8e9b7ee17cb82c0fd5c2c68d6","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.9-beta.1","_nodeVersion":"14.17.6","_npmVersion":"7.9.0","dist":{"integrity":"sha512-9Vt8xjFexLxL3YkOuTm3KodmCv56VI1NZk82Pb59nSc3CuWWsFWN/nSuDvcF8npAeQ6gXt20lrA4UdKS3bQK+Q==","shasum":"9432ffdbca8413c69194ae5f60d82e5122aeb942","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.9-beta.1.tgz","fileCount":96,"unpackedSize":82209,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGIwvPQXTaspy7tGKvW760lrwuvAwn8kq4OSfuQe6q5AIgTC4WwAQjsFc6l6lBfh90PrKSeMjWzDqorPxEseaW2lM="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.9-beta.1_1632916937008_0.34895054710057516"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.5.9":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.5.9","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.1.0","@commitlint/config-conventional":"13.1.0","@semantic-release/changelog":"6.0.0","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.32.0","@typescript-eslint/parser":"4.32.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.0","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"9c8d65a0ad7e46a1ccc8d4d7781c5da9aad5302a","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.5.9","_nodeVersion":"14.17.6","_npmVersion":"7.9.0","dist":{"integrity":"sha512-Iypm4U3bWvuyLPqrlOGGW/M9t8+8PbNt4FUQfjKqlCKauvKeJuZ0MAS0aAzcYM4Y66FnPDxJx3L+8mx1ABpLcw==","shasum":"59d039421f6ceebf13e628913c2476cc7a3737d9","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.5.9.tgz","fileCount":96,"unpackedSize":82188,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDuNRu9ye0j9/P6x7Qm3wDJrDH9/wk3pB51bzq3xQbiBAiA/njvXWuaxXw6gOkMLNC+BeI8gQVW6W2qX1VlkZF6g5g=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.5.9_1632917556888_0.8446097859155735"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.6.0-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.6.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.2.0","@commitlint/config-conventional":"13.2.0","@semantic-release/changelog":"6.0.0","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.32.0","@typescript-eslint/parser":"4.32.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.0","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"0a5ad987e4365b794b07507a6ac3c94f7c3fefe5","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.6.0-beta.1","_nodeVersion":"14.18.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-OPJZz6hc+WSRjPK163rWWG5jGPYu+0WHwou1vaxMrfOeh9UqAi6HxSVryL0g3CECf8i+JyYHQo7IVtTIHVUhFg==","shasum":"62572701784c0ee3d5930f2dab90adf277367c0b","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.6.0-beta.1.tgz","fileCount":96,"unpackedSize":83187,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMZanF1NvcmidFSyOLHw2LfmTDjenVGGZkh0Ic0QwPjQIhAIOqIxXeBJJFtrI+6/jDMsIkIXIqtAujkbwzfFovPrt6"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.6.0-beta.1_1633617160771_0.6671048743812593"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.6.0-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.6.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.2.0","@commitlint/config-conventional":"13.2.0","@semantic-release/changelog":"6.0.0","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.32.0","@typescript-eslint/parser":"4.32.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.0","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"6425c4b261b7940c937aa1c42edbc899e2a303ae","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.6.0-beta.2","_nodeVersion":"14.18.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-vpoWcS+l4zJQYl4dx9L8BDMRFY18iCmnXvuj06kvhyd6Vu+aTdsRKg+NOQWO+zmeXbgZoigrqy2qBhM9SxySfw==","shasum":"de4fa9ee47097a9752b4e1ccaa2033f55c64d2cb","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.6.0-beta.2.tgz","fileCount":96,"unpackedSize":83038,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIASWAl5sDWX4jdQdTYpaG0SieoD1dSQiHEwyE3u9ngp3AiEA0OiIt2L/Kh608nQu2cYZplWbkDjdAOJWTUzVM4159Fo="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.6.0-beta.2_1633618222916_0.28602776057160684"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.6.0":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.6.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"13.2.0","@commitlint/config-conventional":"13.2.0","@semantic-release/changelog":"6.0.0","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.32.0","@typescript-eslint/parser":"4.32.0","class-transformer":"0.4.0","class-validator":"0.13.1","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^5.2.6","husky":"7.0.2","jest":"26.6.3","lint-staged":"11.1.2","multer":"^1.4.2","next":"11.1.2","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.0","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"27e5c8dcf92ad1fc34acf45e1f1e9d2cf15f3ea8","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.6.0","_nodeVersion":"14.18.0","_npmVersion":"7.9.0","dist":{"integrity":"sha512-2ir5AkkxFFbPwnA7AwS6wRElsA4Lvz5bD/pfXKaBJ2RBPxOqOHtUtrqaAGLue6NvtjmvXjOB5E2Lh6ajaqbrXQ==","shasum":"3c4f8876ff84654076d92ad56c59790bf9514b01","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.6.0.tgz","fileCount":96,"unpackedSize":83256,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDrW89pAYRcpWL2rZiFfE+kG+FZAu9uT0y82JnknVmGvAiA/GmZRNAiRxxtDU+6rg6C8IicXIYFmg2iA590j3+raqA=="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.6.0_1633618755913_0.4008396387513866"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.6.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.6.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.0.1","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^5.1.1","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.1.5","multer":"^1.4.2","next":"12.0.7","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.1","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"gitHead":"f3c9a8f33cfee755afb90d37a8cb66de3dee7eb4","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.6.1","_nodeVersion":"14.18.2","_npmVersion":"7.9.0","dist":{"integrity":"sha512-wv2Y6clUGulHNtDdPnSVTBdMM6mmjwRGFWPsM2szFmr7bYsDJsGVTn0PC9SxuzVxhCNvBSoPdh8kJaUdOuxbnA==","shasum":"25543e2a8f6dba8e0c409be13f4f6e86bd8a0a81","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.6.1.tgz","fileCount":96,"unpackedSize":83702,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh0sL6CRA9TVsSAnZWagAAIYAP/i+1wWui/b0PeqgGUQw+\nYNdrzU+l4YN+/8L7BmRnvCvBFUUZYS/NzMGi3ezo41rXMGYDk6dCcw4PagZx\npP+Ys3gnDDmVY3iOdi5xesodn3Ha42E3sl5mBXW7C2dcuZ41pLWaH/fk4CZq\npRGMl9XmWzFnjX7uSd2o/BJUD6SVXFg+WFuJMGpsWTLi9EnCt2DJ1P/xHhMa\nz+OHA07va9A+CZcZV4pQP/WymCoTOE+SCZx4ieYhfnRpYNGiLtdmNa+tdzOv\nbjJaOigurIUJ7nMrj1btIun5Vb9f/eoErYWnVUuZu7ap7MJ/OmC33GndkAZ0\nelI6afHdx4/nQYxtAJeMV553lplU0f1l9ueop5vPqErHWtr3Mho0TF7SXGXy\nr0s8qzwmyzuAf+f9sVH8k5pFn9Uz40mff5Z7d4pVdFTRU8u9oXqGquJBmAtk\nzpBIk484LXczqMFXeRwYYkzCF+b0CxHEkhSKKGidHL1iQ9n0Mn2bXZmATGYa\n/oOh3DDsIYSZgz/C3qHbxr6fZ+QbSk9vXtegU3L4di3sldrVf/x8vPBIDrXE\npoNEge3M1gcR98XOWGZw2WlSadM2jwFxuDmNn8Xjvb73T22wthBNQ50Nl91D\nGGUaWOpJhZqunr5HrNWAygWcURVOHlSx+HkwftlcW6hB1Kd0MmFynLDXMsYg\nLKD5\r\n=YZSP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICnO9BK4QYMBnSqP2jXfUcvSNKvsmqWYBKz1ctvA330yAiEAnW8XlaFN1q8MdKzl8ukxC+bpTsjuREjLYfC3Jq4nYLA="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.6.1_1641202426247_0.020405280230935574"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.6.0-beta.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.6.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.0.2","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.1.7","multer":"^1.4.2","next":"12.0.8","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"18.0.1","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"166761f4ca7265e35237a111606a582f524e2c7e","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.6.0-beta.3","_nodeVersion":"14.18.3","_npmVersion":"7.9.0","dist":{"integrity":"sha512-X2CCPUudhp6+jOwjt+k2FeN8o3yg9pkeF0g2X6XJbFCax+0iAGQ8q/ZgdoikQ1u8fyXZDPkcUcddmkq5gGqTHQ==","shasum":"42a7d52a96600031e74c9308e014fb3f1ea385b2","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.6.0-beta.3.tgz","fileCount":100,"unpackedSize":85908,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8nhUCRA9TVsSAnZWagAAVGEP/AwRlWbTO+RmQy/ocRAG\nrTwMzwKNmk8lQNNEiEImrPyI3mp403CIOl0sFfWPl8Td8mhdGHtwpp3334aE\nPbsKrJzZkS2OTqI2Pwb3hNu94r9YwBMYTi8GJroBUSSSTgjV1/evQAAP9h8H\n8TbL22kbiHndNs3eXkYcLD75OAdxBf7+rQQJFea14MRrYNKPupED7o0o+Zl4\n2LkBQjTxbL0wvteaoksZa9mWST5GyhOgqC7qqE1ek7WeRIwBIFEeZ3yZaPuu\nDu0r81qcBZu0I5e6DV2JydLvlcwVjZwMAILsJOTGvG8GPvu7kCZqcVbNAeLQ\n89MsofyN9l496oljiDORlxHN9f1HAZKndMwVUQieVtlpR9rm3SGSlfZG5gF3\npFq5HSs0oTF9UeY9EECKI0U2Ahf8teeq2q7wqGZ8JeyZQEkRqO8o/aEwrpG/\nUXg8Tk3mM0A/0py9NnFH1AhA0Gee6Z+d8ISVwWyxjO7nWfFTmYsdrasTWryh\nPKilSQ/l8RSJkD5JpOLF8ogryfAtquDyudAbR1+PDue3ZLFyN8IWNG5cJm+k\nqoS4G1bsIPMEnrd/wFtctrSG2UtE1qELmcUekHbjS+/Z1uj0qRY26bPV66ej\nECZ1X7GbvyaWfpiMeZBcac4OQTCcN7/qph5RuPbRI61HHLu0nH5PgaG7MkVn\nXj8I\r\n=TpOb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkW+8A6OujbCWUDq61IfIsbCGyZ1xUXEPzoT4OFbtcIQIhAPTtPt6hvVXckxMc871U7pqCsncJg3X9/AbkWXaZp2Rg"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.6.0-beta.3_1643280467997_0.7761752803826603"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.0-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.8","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"3c36154b5ec6956ea77dc55437e461ae0f9cb677","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.0-beta.1","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-fV7T+c7v32fU+Tq3q6CFyJi+fT5f+7aUDZOfgYYUWfMf9ooL0MH1pDJte1rZk60/0Utsy2wJqLvPNiexF+WFHw==","shasum":"bf8f2fdb44958d75fbf2e1d0acfe6f03ec0bf27a","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.0-beta.1.tgz","fileCount":99,"unpackedSize":85752,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8o9WCRA9TVsSAnZWagAARQYP/ioksf2zt1DKncU8QJh2\n3IkxaSaFb73NNiHvio3BWZFhBXHnAmM9IAXEdB44ndH0E/hkyfNjHOE9G+LV\nKMCA+HIW0SO080oqovEyhg3mFYIICGo1sy+8pqF7LM3BIIR2RQoT1juCgYb3\nP39EcnlT+NEqec0y3Sr77hs4aT981GFdr6Uo8BX/V7mczfd9iJY1h8lz4Biw\nYtEbH7vgTZk0mgE6IrUAHgjGOJPULZ1ZsmTiZes4j52RSFpOFBV21TfBGApM\nP9R8Bo0pbthhQu4iRSzgb0+OVPkzPPpC2Jezzm5fqegSGkViY27Fw6FMg4fI\nfUyM1ubdjb/JqzMl/DMRFHMQY2yYykGtw0p0Ro5iHiw7cg9o9aJ6bbBNocOD\nQHGOAt0dAjnnU5PONz2C8c3t2mE41u6letzhTLc6Gy940Rj6JxGaUmlhXKmZ\n2pBjf8WIcel3aClEyEbzggjPtuVMaJEBUk8+pPx4CHzm1JeeyVBquWwPFKLB\n2gLKMVkaripdEoNVhyWBc1tu6LWMHqktAYDyG2IeY4Px0rPlX2dXj/GXAICf\n/+0MhVMcZSJvM1ALmjCy7KJDx9iif6eDutBmbT0KT+ATM24AMG9K4pX30FBt\nsbIpxqW6QQv8JGjv6xP5H0tw8kVUuV910VhC8ifAnaLfPryA7meIhR9Nlvtr\nxYXy\r\n=WYEp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCe27HfOrM9BpRF8SMReegk33UbziB20LDc+Ed08eiH8AIhAPic+VmC9sh2G9HtK+LdLesI79YIbGx2mU3RtWiVn8Aa"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.0-beta.1_1643286358362_0.19873406793049586"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.0":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.8","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"9eadb6e6826c51adaff99f724cf997ac9e502415","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.0","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-n7nJ3pE7H385xFAmBSUQE2B/Lm8eqnssrh3BPGpMqzx3cSVFL6pTk6kHoVE2pzTTZMqhpg3wg044Q8zzLoK83Q==","shasum":"1a6debcd3fab8841d92243abc4f9c7113661781e","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.0.tgz","fileCount":99,"unpackedSize":85745,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8pC7CRA9TVsSAnZWagAAvOkP/ifs39KaRcm0AGFYLz5H\nTLBY+Gc6/f0hRmmOXkml5l9LVqEeGpAXYZookFayrlfm3zaX2gePfTHm26C+\nHIm6L89omEzBdLeyzWMAAUtDvcvdp7xdqw9RDz2UNx7BuyJfF95qXx9+gzAU\nfxQc2X7TMypdzu7kDZzHmXAIkWX23ED2DCnpkLMEoqTkRKiZFJf9QYy8QPi9\nq1pDXbij3MxV8aDv1dpPjtm5RTyfWKHkr5tIw2q+nLisZsme7cR1pNa9FUj+\nHmcHPQfVuPYNawfqSPJ/mgNg1Z1hut1oUOvLgO29lk+G1whOldljdTJ7H1tn\nHs9T2pTL+ForuERWz8f0kFuCmb9uWgzcZOD2FIxpFsBMhw5sFDDP2EDH1Um/\n16xBLPnYM+yNMqj0M+5qDsyBWA5LUEXVWHj1RTCIfDQNGS7/0cgwc/WA+MaX\n4A7bSesDw+tP4WmTBPbfY9hqHKMD/IvX2vawuqgIlA9fXloDJfbLh4rQgkBX\nsr9D7DPtUMJs4bKTg78le/RAJeipN5lxwKkbE2oU2nlHnMsE6Pomo0pr/8VD\nl1i5ELu8PwPa51Q7BtXN6bou97ClBttpvLU1tLyrsv2rO7nKOrEriwS5qzQG\ngxDhn6qnq2v4aDRqoapY78/ct2h9G2JSPpBqO2YQBzKrc5MfDP/FpCiA6cpU\nJlCk\r\n=TMdX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/8t2uODORuV7u+nqNYB7+ufpwCwU07OWTpNDGzGFldwIgL8pceJVoLCWUnkYf9MXvTFcwFkng30eVq/bkteobcrM="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.0_1643286715568_0.77959979494389"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.0-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"3668c814bb1c24245e997921cabcdbcbdcff5d2a","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.0-beta.2","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-uQdYSnANNPHuEKDkoNwCI+oO/U+42txvmzVat0CPtvIUcRdLXEjONhzewFw1Orlfjqm5lFtZ1VcMo54XJ8mj4g==","shasum":"ad512d0b8943f52dc0f4a7b83cc031c09437e6d9","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.0-beta.2.tgz","fileCount":99,"unpackedSize":85753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+B49CRA9TVsSAnZWagAAD0IP/iWu7ZRbjB8XbNkRcNkK\n+yUWEFuKB6xJH11VdIB7qlf2TXG9w0AfTENUEtNQ56In3pop3zVNMqPCL5K6\nzOHiWn7hvPQielAh8oYPFHtzHa/0j7lVGGfSE2vWT+JjCvLf5qTcGEaDmY9t\ngHyHM//B8dZ8qzv9CfwJr0nnph50K9U/h3dQ6jEyH5hrgXyP9JdUHyupqJL+\nKNXaaciG+iZmBF5vxiRFipDUsAQRGpjRWm2k6UkRrXlyIAihvdyd9RO1qyPZ\nii2ad6aHQwo0W4IwPmQGn2UgcJYK7iNwMlff+916w7mLdBwYW6BKiev/Vs9x\ntoC9NkS7jEYikJhssDN67YEXH8PP2TpYUfuPXhaoZsOz/t8JGGw5JjyzBJ9g\nzMavEMJxikidSh0leKk2dizRtF3Xmom+FFzYu+FjZ64LXdop5r82yBYzqXHA\nlNPocKGXGFZbUKgCza07TSOS888XIf3VgOmL/IACrhZ3TDnFrKLmcZ9eQQeA\nTrl2ao3skYWty9JF1gqmGvAtyh5toXHkZa4VX3Vrgck7WYbHlZrjvCoMgadh\n03PjXA2YWY0dc8y3kymNCKbxJmknrSzvetoF18ZJvricoKzt0kG1fOMK7y/Y\n5huKecx6usHvCQXqd0cYUmOjxyfFoKOYf/EYRjze10z4/SqktsNPB5HhNjWF\ntbrj\r\n=HE98\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBkaxr4tT/U8t+AJWWaEuaWnAD6lJTGskGwUmnLufVxLAiEAk3jCnezhByXwhkUX4OoutAoTn3Z50tyw41hJowAD17w="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.0-beta.2_1643650621499_0.6773757368245725"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.1-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.1-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"4a4629a34e476e76dc526100ee07936f047d3678","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.1-beta.1","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-80fCcHNm3VLqP8jwwXwk+cdIKS5KYOIxnWzMntSrWjEM2SEpK/aNb3b21Bq43wmluRdF/qCY/FRL6NtHszqGVA==","shasum":"555cbb65811ad099617e9d74a53ba2ce0565a85c","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.1-beta.1.tgz","fileCount":99,"unpackedSize":85753,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+CdUCRA9TVsSAnZWagAAA7IP/iUnXIsZG3i3xOY1SSrA\n+hijr5WaSLR2P0xurEsEXFVxRpoeZoiFn/JsBuBuqP6iLV658TCXSu/aBrr+\nIbaabRsAMbNBd/EVIyPHVSmhPASboW1u9Ek1Z1n1M0MU36qfn1gHLVh6L7xh\nrgR9+kSERPcblKt+qF7BXEW52Kf4TdFIUtz5ZYyBg6jl8DsHX0f0q/IVfwf4\npCykyw8684S+RJWilevII2jk7QML57QP6vj2wi2ephY+vKY3WjrSAXNFVUR6\nY9P9mAcC8FqzKXGrLADqK7jliOrtUn76VWylUkq4CxXiUD5/Dd6s6OSFDjok\ns6V48biHfcMEfrC7lOFCIPWDO7D7I01mtM5Z0zvAl/HIV2wteexEkFgKmxLX\ncvnI78HemFnW57h+Zig9cK16//WLcv7MhT38rpI/SuDn76VgraZy9xldTxt7\nFGg0Avl1ACyWAK6W2a7AxDdJ8bso/wQwnOXwEYpdbSSxyA+7kIaUrM7fSof5\nujJ1+IGeBPNVLmsDyzvy/+xY1rmNvj6rpTVDqTgw5hoFMq+oClOzn4+e5Ovj\ntV0RtdesicwjJ0oaGz/7oJNhsDRsRWqs5vIJUMnadmuIQzTzNGuIHaJmyB+I\n3OcTxSvpwK1DV7x1Vs52QBvXgt8eaje1zATxmiEk8smeyoXB9KLIVctqsoJz\nZmMv\r\n=y4q2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMBnXmZNT56COKIK3/+f2bpdLagx8AbqfnOpICrm3SZQIhAIR4Dw3uCUb7WWghd5+QezsB7ROouWlrcyPsyQVGhUIr"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.1-beta.1_1643652948169_0.9507242221083052"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"c91882d30a726e89052cad1ef0314c22440323af","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.1","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-Aw081RLtfJr4Ccpoy/jdOnIO7GQ2luEjiQiLYxGlagp3QoBvB0MnE9vaX81cTvVjkD61pN44Iw9or7ue90n6pQ==","shasum":"57208a1aa6f54056928997117e2a08d92d27d79a","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.1.tgz","fileCount":99,"unpackedSize":85746,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh+CftCRA9TVsSAnZWagAApmAP/ArxVS147U2c3Rdj9RTL\n/vJCRM74yDPXaWdB3OTcnIsa2Fic6fgc7gqKy6KMyUqhEF0CtDsINRcHUHQG\npkzgZc4MCaWS3kXxU1em04cTHyJrp71dKB30dMUZiKa/Qm2RhNkZxt0h7zCf\n52HIxCjKCMNqR0jYWs97Yp43KBp0c6j7iBQcsIJL91WrrijuN9F51+7AeTQf\nIOjuv0OmsupxWwne1LTdwcqk/KUGKq14VOgWM2sQ7PX1HHpJO+lGKG2f1qHQ\nfHy4hkaTMNuxBYFLYhs/qAS8ZQblEi5TkS5z0kti4gjyYNr4ef+cHmIPrplH\nPTgZTftMVjjY2vFOsYqv9zdxGWy6CY2im4JkIPjwE/AD6r7HbVPr+pr1cAjC\nVrE8p4HenxTxS5n4IBfWc8hjTU0Mg57w/lWhrQF6hs3khf3in+d4Ng0oJW4u\nRZyRalnaiIwRGbSVW5NOYoncGPVNnfn7LiCaz4mIJgTdakHNmi2waHEopuP3\nbHxp4tIm1xcyxX49wP21eFeqeA5yE2wkIl1v2eeaibnkg6Fk2b/VAAGQGfYt\nTuAlN5gDzDqveYL+sbCpOUWWPOAQVyiENoloFckiCGld8Bmu5oyR4wouF8Se\nJiugPUxYHDHX+gejBPVV11jCnq1oqogvJeO3+ueNYQ7CDHaVOK6ycZvgiL55\naFab\r\n=FryR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3p2+JslTOz7/bID+mYuW7Qhojri5py8LHYzvsN2EzggIgHC1OUbrQXTz7kwXpnBLd04wxLDPI1o0pRJnebfzIVLo="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.1_1643653101219_0.5755577133154126"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.1-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.1-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"1befcb43f1a37ba4c015c3a49872452d95fd8eb1","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.1-beta.2","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-EsDlwfs+HwkBU7t3XnjRrOhhk9rHNY2bbV7b/ZZYPQAIXuTJOJLpei4TjtCgMD2DC4imVTiA4AQTWwMkgxynHA==","shasum":"0c0f6157be43cd839ffe99d803a230455703f6d5","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.1-beta.2.tgz","fileCount":99,"unpackedSize":85755,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh/nqJCRA9TVsSAnZWagAAu48P/2TpmxbfhVkkSLaTeNwO\n41OlOTRdO7IfunAkZfSORcEoypTtR5RmDqXu24xVGLl00xrZp+Y8xvSomMXY\nzfqbTDW5sNQGnjBBZF1idYIeNoVPcCcIHpXUYHbT3UP9cj8yTULbUR1LH8ur\nr7oSad2l5MS9Yb2rjzPtwDCPG2OubiQe/+3XS8M4hD7ua0fB8b+Im1FEMW1X\nIhRt5I+K4wVCUpHqx+SfaH6XRFuvfWVQF88HA90usgmAC431JXE+9d497Ii9\npHGXPasyItHPidujxS5yE0hHCelei5Au/1s5GMK/0D2VQEnAFkeaX2EzZOfD\nBm9Em3nB4RExS+svosDpq++UnudSJryIrca/VXr4R4oDDie5rOcq+xpPXBA7\nIZ1PbURMHQt8gJwKTPjj+4tCD+T6sl69uZLk97lwnT12aof8MnqgeU+dM3e5\nVCHxqCVVvwK7uJImiPdhORV+i31DFzuf374hJ6T3794e4aPXiPIxIbhCt4b6\nk0LYFSpjQ9qGCVOI33XgZkrTSWgIyYgxhwiZpHp9q8JyY9F+5GgjF184Ms6g\n6/BlzCAwysItZr2Wc2x1JHz4SXcuOUPVciCykp4n7DLHtn2jJYP8Ki+HJVXo\njpHuHgXbuTRLd1wZVn1JzpGaQtYBCq5ZMv/pEA8ilAiTMoem/WCzUIs9jTcp\n+tip\r\n=36RB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDbIEkvvGrF92pOObHi8BtfDUhW7ItcvXpWbEHoX+BZ8AIhAIn/GH895KvLcAPhiPjjvXHHyMxWgAZFwycKVo18DQ2t"}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.1-beta.2_1644067464901_0.5788320869248493"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.2-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.2-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"f97753488bf05be4ea0acc58b7c59baea54404fd","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.2-beta.1","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-YFXLNHk1cReIWaznaNfzmz4L/orG5emMY1huBKxPVmC3PR6Z4E13r3bUrCoMgfNU5NSVY8PvDA8Em9TRQjCc7w==","shasum":"8ec296eeefde5a3c5eb0e1bd201c41d128e0def3","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.2-beta.1.tgz","fileCount":99,"unpackedSize":85755,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh/qNsCRA9TVsSAnZWagAAH+AP/RNeb3FfZ8z4tz/nP7mv\nKyncg6xWJQNMZcenITR5DjFB82LIK+g85BSItXAQvEeJ9BfZvLTf+v4ELHnx\n8fQ2bWlV239NVsj+71B3aQTacpcUFyAoRi70alCimqgkSVSwzEnStBm3/Id+\nT1FA4fOil9gCTgMv97086K1Lg4zcloABcmJIExbwL7q/GyN7hnEDOlBOhMbs\nAMpYlam2CTPhDiR59n3weTLbVbW1XKF8j3/3dFzQyhN1nL7S6bsrwhqozitH\nA8ZsCgofp9zqwF8TY3lOBclNtzcuo89YcmxvNzCQ9qkIP6XQXKrXywCgJYf/\nlgDw+l8uP6nB5tTP/1mzlXBLgf+w2ulUARw+avgdjbdg+jkWeQFpDMw0ihev\nN2Jsc6yw3qrGOaRl4UrAT9r8E/8frQW8wiatEnbjNVhHIfHolbKIzGSUgn3M\nN26/IBffobrspSw2pOSTcaJCXLi9YJ8SNcsVErJvYlvZW5OGIVJkIwur0Pqf\ns9P1DDxylBwodUSDM1RZ4XBeAbBbgvBErflbvuwSi+eLq6l7w/O9dbh6ZIRq\nvZpEUpRB/AIxO2Y1ba6twZwRwl6bjwLNEftj+5kqfXMuIpolWSiiMywzzCMH\n89QH0RFD7OjobLN+11ZRLiWpZW9aVoGnH5C5grFk8C0t3pIPH3cneYhl05yS\nPWON\r\n=GWds\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC+slS1gxEMVA/YrHH7AM9LJdxkvZAwsHsxk+d8Sb0eKAiEAo73nYc+pjy35SlDRQtSqnWrl6FRRvR0/CAKIldcEl/M="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.2-beta.1_1644077932272_0.8687886707135171"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"16.1.0","@commitlint/config-conventional":"16.0.0","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"7.0.4","jest":"26.6.3","lint-staged":"12.3.2","multer":"^1.4.2","next":"12.0.9","path-to-regexp":"^6.2.0","pinst":"^2.1.6","prettier":"2.2.1","react":"^17.0.2","response-time":"^2.3.2","semantic-release":"19.0.2","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"a2673d54a393e6c545f8e9899c7bdfa7e14ce7f1","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.2","_nodeVersion":"14.18.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-/xxwCMvjLl9f1PoloG+KpfJ5AOzdt3K+OJiQseRkD2gXzDjTHqdRlibnZJHmpUGwU4HdIETjbqTTJxnH/tkuhw==","shasum":"8c2bb405ebf755a35e70af13e0a2c43ebf5d1b6b","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.2.tgz","fileCount":99,"unpackedSize":85748,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh/qPMCRA9TVsSAnZWagAAH9AP/0vxffkfcKGFAlb6zxSA\nrfkXzeaYKrNhDGUTUy4EMYMFiWb4kIsLWiTivrT+XAe46r8rEegg4DLHlaPL\nLkk1cwGonPqLWhKEzFG2LzT9xzv1Q0ORNm9ZUSonM6iIhAVgnZZU95Waehu4\nl/MbZchFScWot/l/CFxkUV4FmWnFTrMqzECcDFRdEPcrnPDvUpz2c35rm9sI\n3WK0ft0xqqoi+cw6P0qMKzHWQA7eJw2/WMtu3Anac4vxVC+O0MdougPNXEjV\n+oo7X9jBh1yni9ZVs3kVBNhnzV1FduaSXDOrt3H7yqoMvnUFXaAtzDRmcACv\nSve8EZXAboE1rFe4lf9UprAm84Xt1d1uKLKbrLs24SD0qxcYvt09PGz+HFWY\nV9vk3aqHTW+IGYXOlBzkj/+At5of3nlXVxtfvBvb0wmS092e/jRwwBMxe3hq\nEtIMQn3FVsCs2pPRX9VUSilwI0Oo74KDWeR8Mg+7pxAwzjHXdg4QwMIekhjb\nDGf8Ei0BZ0RG4SyZVC5lf3zzgC+HJ0mXh0b8lo3annwksGeDTL6RL/ygOqAs\ndb+o+NKfsmaW2dT6BKQOMK4SqNgyFIZi503v9jPn7Rf+EJjbgsmyrthHiLDF\nPKpqq8KnjnA+7eBNsKhDV30vuvJE7+aFTT4Sbmr4f4L1LgIxF3gbQUqcUMEY\nz7dc\r\n=S74s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiHgK4PPvcjxih0yeG/RszRZnd/GAvGIx1KBvx+6QsMwIgOemKRFnVHxHkccjA3LbCej22L/2O9u6mZ3LWwn+e690="}]},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.2_1644078027994_0.7922530565415953"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.2-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.2-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"98fc8fd2055e86a25abca7f3b35abe234e85f45d","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.2-beta.2","_nodeVersion":"14.19.3","_npmVersion":"8.3.2","dist":{"integrity":"sha512-Itdn7EWZj1Wxhh+wVFpOHq5co13SdTNmnlRXsFklzE2Rx+2KL6h9lETPOAnJ8EcLjekFKRzLVYAvst1vfbltbw==","shasum":"242c1a46f7bab3e57251a985d13f5294f69279d8","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.2-beta.2.tgz","fileCount":99,"unpackedSize":85774,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBIzhHjOn4RRvMiKJi9OuKSp4FqOdCNwZq6I71RX2MyAIgAfJVaeyOCC0E52+dS5lj1bhsaw9sUr8DJkwY7JmV8xw="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqF6RACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoC4A/9Gw0U+mtNzyYucjiefk+4evZ9VL0T8tTbK/vXHSBEMRJchoQG\r\nIrzK2lImCRVWcbs4tBISXw6JYywuTVqT8BIKpY33ufeZBIuOl4r2nwxBvhuf\r\nZADZpWlPcI0ceLHEIO1thjaN9vr8Z8ngNkokbVrLVGsF/dPNzhCJk15u9bE4\r\nnSGJ3Mv953qkt5VbQJXUDEnMuw62nfGCv0O3nzZLjAWOq9cZX+CtFnpQXWQX\r\n3nrfHxZF7F+XMzhXv1wsr7Ggna1b+Wi75S+V9E7Z75HCLUzbOrpySkhqsuK5\r\nE1K3OdmEy97pEmGrucDF3dN8Ru/IdU/Wpzdp6sbYvuJdVr5nwdGw4z/XeetA\r\nDQNDBsC1ViPkY2/T5CeDI7hC3ytcaerKaCLjnD3BLH87/C6Nh0gp088m1Wsq\r\nSOdSA7Wc2y/ivtQoOKrjyoi9Yn9P2GrJ4mxMifSSEKvg9OVD97l7zYUNWuu1\r\nadO6gxlUb/k836P5k7R+ek4Qu0aZW54qGxTnYCf8A+BGAtxljiuQXqSusk4Y\r\nKJZBi6WrERctdLjprLoR+Fm0Vuh+hxAgIZf6kQbWNMTKkAoxYqoFU4SrN166\r\n+bn2FdHcQEl6CNeejdJUZbEeqz1K82zOzS6YmJVWrMsduKHZHVrBf3UgfUTq\r\nVIfP104i/KDqGiYdFZtVM+D3bED8SXceINs=\r\n=bQFx\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.2-beta.2_1655201424814_0.523564034806659"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.7.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.7.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"b524b9bcbaf8aad6ebb923ffae0a27d316c8c563","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.7.3","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-sr1aoC1bwZcAreyf984CLMoTTWQuTp+qe4qBjG/xG99zscL1IQqBQmv+GikyD+5ToaAc1FTqGY5GQt5go73O6g==","shasum":"c9f27c0a145f3f2bb3d6abda838f9d702796a9ca","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.7.3.tgz","fileCount":99,"unpackedSize":85767,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDA7rPprs1s1AzC8u6RQcS9tvpvS1BtXprAoFWtGfwyGQIgUoTJhpDt80hnQyDWeHWQCTqXhb9uAU9d55jV7jvaTP4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqbANACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqw2g//WMOwt8HjvkmjyymfrHWsUvccXQJdyusC0zhKHyhMj7eZD8dp\r\nuWtPWa3Qtb6cYhqBlH14vtKxzjxEWB1Zw3wb7Pv+XdxGXsyvToy/n11t4CnT\r\nDOO9PZA6Fs4p/z9tOMCMmS35LLsaZ0VFvOZAqGKTY16+hkEeynRCyPwdkT4L\r\nq88WzSYe4qKbz8CYPhZclTx7RnNoy6gEayRtXmN6xPSaqaEmcy9Vz9VAVF/Y\r\nqJ26EkivqcveHEegZG+C9wtNVJMW13WodH36QZnNfVwlJ7bxokNxJIois7Ne\r\nK/YBNEcjR+uFm7OW0mAUMGXtL12W6TWkDBLajkPmJHsuZWgi5aizwNA3N30+\r\nA+mKCYt1rKoAtftLsDFJP+Q5kn115FEevVJT+OQAczDqbaBjvliTOcbM+c1m\r\n2bSjRqVXZ9d1bJrBQnwWzxYttSQhMnBNpdfiL9mGo89AIifwXDLTHJuseQ/a\r\nB8aJdz0fk2CXs1U06JXKbuQ9JDDoIoUBiIEnGbqPfmnirTiuihiz9BxOoJwk\r\nDZJTdYK+UxRke7nLGf2gx3kto5tXK5ErkLttprSBR9No9rhx/DcHU/2oSKb1\r\n+MeLd1T/cldcYGKCQVIQpjzdq7IxsCL4v7ezVvWjLrDCvsHNG8maQKMoFC1m\r\nR/v5bblDdRjA/F00gY0eK6i1YnLep6ve70E=\r\n=Aghz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.7.3_1655287821019_0.5753969332787352"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.0-beta.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.0-beta.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"650f0a51cfc0426285d4889627c3b16e74758f46","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.0-beta.1","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-5aZa2/5nHSM+luskO/jrX0krLvqPIz//wDfh66+GdTqDJ/ZPU9Vkp/pp/nGD4SdLZmNXWU0nK3f1GOrVd7qF7Q==","shasum":"a9451f8a3a85e0250e9b2b3e03044a4c7fd650b0","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.0-beta.1.tgz","fileCount":99,"unpackedSize":86079,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICv/a5Mdx2N8pu8H3/ADxwYm/eLNMj3EJFQENM93oWksAiEA0L8wNvJtz3BQG5nou0yEAZ3FoDKoYSSxVIMj2lcPha0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqbEgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmos2g/9Gwv1u+iS+aOa4JaX0Y0tKYwRmLFNYKJiABIB7nJZU5bSskJI\r\n0zvlLriW5R6qkDJoF27Hf5mZBguBZMigjtdQlhInleX9Yl8WgoKnChB3DkCW\r\nXpKspp1jIlJeWvuQx4FtdU5Nq3RCJ9kyhIjp5VpTJeyo8BfO8djXLf1L+obR\r\nvfEHdWcpDQMyIezMZ/fSEQI5yOA4GJS9f78GXKSadjJh/t8viyk2Mu5tvVkE\r\n7xMkIqgXhfZWChUCot+9XFb3TrPWCy2cg+YjzhKimrmjmJ5pqi+0ktZ9oi/s\r\nCQeDyf+5HLpyWKAiHrPo9kUDdztyo9kWP1EAS4bjoMzjQFOMoK0UF5l/GO7g\r\nDgvVpwottsrXHb4Gip5ApuhJSL6gm56eOxVyY5d+UjahSXSshAdnkagCztNv\r\nAeLoAU8gJqxLdmPXg44Hk75ydZ8913LF2b0UR4ySlPypUhFrzepgq9bA7Smd\r\naBsJ++OgB0Xy0TooVn/Tb7dbUWOOxtN9BSJKu2k3UD0UIqw3jRxhR5ZcVlWQ\r\ns83qQ9lmJq3OoizV8i7LloG6yX8ew2s2F+V91oO9nO39dxsTX1a6H87GTwqa\r\n6Y0F9ssEGppoY+aV1wIqBhUzFR9l2wnff1D7hJdPzrqt4n8xz//u33pR59NU\r\nG9XQu2EFe6Z5GV+/easuBijN3KopQw16T5w=\r\n=drDf\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.0-beta.1_1655288096540_0.2903889849662824"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.0":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.0","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"7a1c48aad751739c6e781b1cd25a03cea524dc0b","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.0","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-xiV8jLP1C3CT2Kt/lFAp3OotL/cv0A708crd9i0IHZDK9tw8bYZ4mU54O7QHljB7Dm+UWg3gF/sYqJ1+Nt7O2Q==","shasum":"6b0120962d8a4c6478b7a1cf662535594410e208","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.0.tgz","fileCount":99,"unpackedSize":86072,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIChaQGDh99ADGPO/C+1ULua++it0luH/GdihlBLTBNZDAiEAi2N2pbIf1tAlBnHTcZ/DsCbyUPH9hAz7B/aSYrSiImg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiqeIOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoMCw//f42FTi833sVkkeqZsUBeQVKmQABHNmUC66tSY7bzrSMcsmlz\r\nNWOMz1GcH3IXcN13OFx+juLJd1hyNXeHN9pHcjPQsQUyFgqyx/rM3l6PeS6T\r\nf/NVDuWjpUW8YanBaDpdbd/4IMtulFodu6Odt+0BL3bVYRsg1F74cdsbt7KO\r\nn7F+vVjevySlh53mREedYMMDRSWCDNv1+1n6IVTMr41VSoJdL8snRQ9ivAb7\r\nTnaTY3ic4BvYuQgu5j3Z257bSHgfIgmf8IzG3A0n2y+wqEsRbbximzyVfP/r\r\nrw+pJdhXWeLS0wkdlrP/LtofQIKzk1BXgxcB/DkZnmmLRwxpQAXFgvrLfL9z\r\nlH+jT2+56idH3bSO5GFO5Bi+Wx4Z+M6u1MuG225EFVqeLItfrvS4Ix4GO5eM\r\nNgRNMGGf87sQ5h1Q5VgZQTePLtmu19l8KsUMttYjLJFhpAPHjgiXDGOO8P57\r\n0GnJOhlHN0NieEGatXOoo97ZEBtX+fcaf8Nt8w8wt34xsaJMUZIlkW/wH1qu\r\nXbILY+1eknelX4AEiSkDJJAd+eVFNHjSyMbRjw7VhHCYTHRvpMi9o0XDcWQy\r\neXts+FQfQ0S/oA/BKfu7ZWWK+/WWrSpMlHxgJ0+joJkAJAWB9ESZaxv3ZRrn\r\nF4V6wCnqwFr787u6qPJzRsSA+45X456suI0=\r\n=mHGt\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.0_1655300622654_0.8564040597645886"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.0-beta.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.0-beta.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"352476a1e7718ff6dbec18527bd66977dac43db7","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.0-beta.2","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-f/ES3IthY9OoK9mWP0wGn649mHSifzOJ4S+1ns9qgXtwWrJRWvLLNSHKDi4lvQX2HZJ0GIyjKoIw2gCzWsZjVA==","shasum":"6758b2d4a397d112ed8b46b096cbb6d3725157f4","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.0-beta.2.tgz","fileCount":99,"unpackedSize":86191,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxe13xZ5JHh/iNd+3AZoIFFyovlvPKjhafC9WE2ST0gAIgD6nf+KdKqZ3+0JCgKh7YjAdy5r9LqpJanQvrR5Oqn90="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiq1LcACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrPdA//V2yd+oAUI6DJlG4Hmjz7VpIOnJpqKOs1x7ZeL3muX81Qdf4B\r\nVPvc29aAq/T2afwuZy1hoReiuRNAJyEzbxqSJ+n+5q8a7NuaasNxESGdJYpa\r\nLnoe8UG3P+aMWV6yxi0IBj+9qawxHo36IYpNhWQ0oCJFZj0x9RHxQpDtz3xy\r\nVxEXTOIu9wj8kq4WDSigTQCbSiWymSWgnB0gh4mW44wCPAfizgpf3ZdXUZV1\r\n/e8ohMM81F72aHBbt1Gole5sr2aUbiixxiupY8HVp6zBh9STl2YxMpXlwiHA\r\no7Ju3IavfcxBZ07tiPbQB0KMl840t5wL3rbYs1HLQ5aVYcjoct/kEXgbp4Ev\r\n/FsacIpbTalad2IvXqdPLj+q6pOhDdRaM+XY4epf9cD1pyaAd1UkAT6Qj6Hy\r\n2ZMruQusE8vLGEU5XkOHU0v6nIv1dLbeVLyfLCB6Ch1cOmLF+6dWF0d8Mezm\r\n4YMvJd40cPaBiS5+JEj3mNvP31aXr2mIULqmYp8GSyMahloDXIaT+SOzflRQ\r\nHbytnN1un+FubN0vbBgwGNlhFtYV30s4lV7CkGegorqFO5DoSuuPS37CpE1M\r\nIoHEclNB3tcfm405MwSkhKpD+mMiLyTqCQO1fTyoUJpleieH8fYKfPKhmxhp\r\n+zoH5pv3Xol/x6urt6W4DigP166JRWmweY8=\r\n=Fpyz\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.0-beta.2_1655395036011_0.6299239095331037"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.1":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.1","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.1","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"60668c3f860d418ccb5c731137fc60681c2cef6d","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.1","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-6+ls0NnnTFlzODGu09bzMnPXq8z1rUPmR9D3J2wD1AtmDjPjV6FsL2+tT4EWKnHdNGH7FFotIZiUPVuIpXJ1sg==","shasum":"62100bc2e42ddeaea06b3a1566b8109488c06ffd","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.1.tgz","fileCount":99,"unpackedSize":86184,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFJPOkbCNmKbhouPukifYPhyQbQb1JqosUZytV/5UD8sAiEA4XRPIHExv4y4gRyrImTamYq2ycsJ1lt7pDUmaUGqReM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiq1PAACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmp77RAAkrQ1WTFRWdGdS0qmXVdH0q1y7uoBtuvui/AKVyuJNHETyR6R\r\n0tdZcC/ahF9VVtTFMP1Mj5G/riy4qRpz8AzcxEOhUEMcabagHDcrJjbzxQ0M\r\nm1wLb//b/YCzJtve+njGiWk7HfQ1OKSceDVt7DpUmBNhNijPavGGiv8DYfLN\r\nC+4TKiyAe4Rfkcplpy720nISDk6nwj8On8x7XaooOB1+RAjlzjUvQrqslf1v\r\nUcKddCiKfqREGAGm72ZbcUMQIVwQTg2vqLi6kfl3AauyG61UAabUeeqwK4qz\r\nNsg26v/KSzFojHW2vIwOKjIwlq58RBmoIdA+LEwtccbTmMSGbxg+rV40Qcg6\r\nkm2944W3lJcqT4/E8G/NXsbtJO1t8kLjzBC4gauvLVchx8gyqtxcW8It3FCC\r\nwdtJavfpqvIWkv+pNgOlMtBjWhGjG3EfkIbiPC6R7c65fWnKJFQ7YvwCotV8\r\n3tcfyAwtQtYNZ7dnGIpkhGS0cBqH+FvT4qZnqmqJqZjyBi+CmXmtZqPKv7Vh\r\n9tndwJML+wujeaX6f/fN9QKC1tJJzmZ0x/618juPZpBYdbbtS2pFs4COolz7\r\nBhFSPjKv7S1ZLg3yw+ABhkfD7cfyH7ooXHbIRm1s/D2zszWnIDJ/78IEw89S\r\nzdTzBkZ5OBxWKCXZ96op7UsvXEnPKCoxB+A=\r\n=Bsj6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.1_1655395264137_0.35970728290442544"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.0-beta.3":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.0-beta.3","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.2","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","gitHead":"a688fbe28d4c114cc3a0757fd3b036f54df8a61f","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.0-beta.3","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-3P7J5H4QVHeVbxEgIlKa7/ilYhlUse87c/JQ2aafla3BzmeeqsTxGgBtgHxFyOSUl1Sj2IB5ppgZoCGy0xO7CA==","shasum":"0a3082cf5f8e11310073d7b415cc28ba2076c7fa","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.0-beta.3.tgz","fileCount":99,"unpackedSize":86165,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC1/q2yiVtte1Asa4eTbkU4EliVefCBMNXFP52f1mQU9AiACgvM8aa/S1GUtzkF0kLHqhvPN8L9sD30iNJT5ZWEEig=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJisxQ7ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrZyBAAhCnhXg3fcqa9tsseZGL+QYmWFZ98qfoho71h/2NDEe/Z4eXW\r\nar6jcwnxGx17FPNrcmIgUNOcAvW9DB4NeyHrsmGsT+nuoCW4WhK5evYjaPGA\r\nOFneJsjQVLJGC71g1nPIMlJ3INZEJMpBIQhplrU7IxVfWMgji41oZzbQOI5S\r\nVP245InrplVc+X9J6zJx1NBLViUnf/VDVz5NDz+mKa36M47PQ1XN1d7QPkIp\r\naW1z9zl3dF4CuCPRRUnLSR5+ecySTI/2CavrZZQdIiiQDk68wAOquVcgiUQx\r\noMVGYnhRAHrG1uCro2GbxUviYx5UdVq79pcHtlWLWKwpy8QeA+yHzgwseAl8\r\n6npJnlEK7DkpIwmz4+VQwyU7EPWjaHppB791B/bSVwV+B5iHh+6fY1VMu/Hk\r\nzpRUF/XeuZbMfwyyalk/bvmMB2Yi2oSPgc4P87VE67r1XJGAiVZ/5bDBAzzz\r\nJ9ecsQzXLMyNPvhwfkDJUQHIklTiiJRDKh+/SscEd9kr9AJRWzdujSYWLLcw\r\nk6zHscj+1F0+ovMxjYK6Vbxx0qDh4msLavvilxxIv2dzjNSSfVkkZ1fejH90\r\ngakAhJQQv7B9uirch580f/NeRflhOCfGrDPGPyJy278c6a1k4XkAFQ5rDfE/\r\n+7MrqCbrBGc0y/lAZhrP3ugU8/ZVSPjkiz8=\r\n=pLOI\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.0-beta.3_1655903291740_0.30192350573963944"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."},"1.8.2":{"name":"@storyofams/next-api-decorators","description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","version":"1.8.2","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"main":"dist/index.js","license":"MIT","repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"keywords":["typescript","next.js","decorators"],"scripts":{"build":"rm -rf dist && tsc -p ./tsconfig.build.json --rootDir lib","commit":"./node_modules/cz-customizable/standalone.js","test":"jest","test:watch":"jest --watch","lint":"eslint \"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)\"","ts-coverage":"typescript-coverage-report","semantic-release":"semantic-release","prepublishOnly":"pinst --disable && npm run build && npm run verify","_postinstall":"husky install","postpublish":"pinst --enable","verify":"if [ ! -f dist/createHandler.js ] || [ ! -f dist/createHandler.d.ts ]; then echo \"💥 Corrupt distribution.\"; exit 1; fi"},"dependencies":{"reflect-metadata":"^0.1.13"},"peerDependencies":{"@babel/plugin-proposal-decorators":"^7.13.15","babel-plugin-parameter-decorator":"^1.0.16","next":">=10.0.0"},"devDependencies":{"@commitlint/cli":"17.0.2","@commitlint/config-conventional":"17.0.2","@semantic-release/changelog":"6.0.1","@storyofams/eslint-config-ams":"1.1.2","@types/express":"^4.17.11","@types/express-rate-limit":"^6.0.0","@types/jest":"^27.0.1","@types/multer":"^1.4.5","@types/response-time":"^2.3.4","@types/supertest":"^2.0.10","@typescript-eslint/eslint-plugin":"4.33.0","@typescript-eslint/parser":"4.33.0","class-transformer":"0.5.1","class-validator":"0.13.2","cz-customizable":"git+https://github.com/storyofams/cz-customizable.git#v6.3.2","eslint":"7.18.0","eslint-config-prettier":"7.1.0","eslint-import-resolver-alias":"1.1.2","eslint-plugin-import":"2.22.1","eslint-plugin-prettier":"3.3.1","express":"^4.17.1","express-rate-limit":"^6.0.4","husky":"8.0.1","jest":"26.6.3","lint-staged":"13.0.2","multer":"^1.4.2","next":"12.1.6","path-to-regexp":"^6.2.0","pinst":"^3.0.0","prettier":"2.2.1","react":"^18.0.0","response-time":"^2.3.2","semantic-release":"19.0.3","supertest":"^6.1.3","ts-jest":"26.5.6","typescript":"4.1.3"},"publishConfig":{"access":"public"},"eslintConfig":{"extends":["@storyofams/eslint-config-ams/node"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"config":{"commitizen":{"path":"node_modules/cz-customizable"}},"lint-staged":{"!(examples|website)/**/*.+(js|jsx|ts|tsx|mdx)":["eslint  --fix"]},"types":"./dist/index.d.ts","gitHead":"2e8416427ccff850d5790e3ef7961fc0fe5834c8","homepage":"https://github.com/storyofams/next-api-decorators#readme","_id":"@storyofams/next-api-decorators@1.8.2","_nodeVersion":"14.19.3","_npmVersion":"8.12.0","dist":{"integrity":"sha512-42K8xxwNd/cIOR5+t4OfRxXTATGlGzY2T/onlvVFLmKG9ci2Y/hvFnSepOE/7fiq4FnYrUA668064KxysXEUJA==","shasum":"f0d9a5697cf41f919cfbd756296f4e9fda710fb4","tarball":"https://registry.npmjs.org/@storyofams/next-api-decorators/-/next-api-decorators-1.8.2.tgz","fileCount":99,"unpackedSize":86158,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE8mYY447EOP2A3euUIjZkIZfpvVTQonyqCnD/nTXj2BAiEAi/LGA9+TmQIqShddsmYwHMGMHtz4fsMIY73CBAGgVRs="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJitZR6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrmKhAAm2ob6UAXq2KWSgPbLXJRGxt1PObSA0/OCX11n4oDRcCGdpHK\r\nDxATi8dd9d0FGobs3P5g6UmgOJbJw4QELK281qQ7KkFk+aShowX0SbidBlP8\r\nSlSdVcxD4XJumjbUOZsdrg2K/njbzxk33KUMVPrtPRFjOEZrOILdBJ/5rPkQ\r\nGfozmA42KtO5Xl8BsgNleDohAFzFYgULwGopOXXg3BTGe1QsWghA00Z5jQ4L\r\nMmTYcfyTMSHfcSXFHfW9Ukj/PsWefBDgT8mikUAusw066vccUMw1XtJXTDLk\r\nxTejUdqs16APixr5XpwALxBbAEwrpXTnISEUNwUD5ofD9FxtON0hGWRHzCUq\r\n0ayw87gT+CfhRLD1z5ycFwHO5GAZAdipD6VC1Ya+mgBsXCc2cWpZu6SfLpi4\r\nu6ayE44T08VHZ7fMrZ2/2ydGNOxsLbTkgJQfwRRD1br0boPGTd9gPeTbgTn3\r\n/aQvJSFZCvy+skvXw+zR26PrpZKNheCOFYHZM2CXEmqhrS1Qhm+MkDw3+Xw7\r\nqeLIEdcctCtucdLTw0QHtMtZJiBBv+QCL8PXeS8zS1+4X03Fiux24Fsr9ac7\r\nHasWrDlhagIoigEWSnpjFSka0L1NJssTj0wCshAljcQxHBsapCd9iCXAvFME\r\neae0fIoOEQDl6HQZzaQKceGFqR4yRr6oqTk=\r\n=/EIe\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"amsbot","email":"amsbot@storyofams.com"},"directories":{},"maintainers":[{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"amsbot","email":"amsbot@storyofams.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-api-decorators_1.8.2_1656067194156_0.2243454920816912"},"_hasShrinkwrap":false,"deprecated":"WARNING: This project has been moved to https://www.npmjs.com/package/next-api-decorators. Install 'next-api-decorators' instead."}},"time":{"created":"2021-02-11T10:23:13.939Z","1.0.0":"2021-02-11T10:23:14.290Z","modified":"2022-08-22T12:22:24.307Z","1.1.0":"2021-02-11T10:53:49.831Z","1.1.1":"2021-02-11T11:07:42.341Z","1.1.2":"2021-02-11T11:38:41.810Z","1.1.3-beta.1":"2021-02-11T16:56:04.306Z","1.1.3":"2021-02-11T17:20:22.480Z","1.1.4-beta.1":"2021-02-11T17:24:53.338Z","1.1.4":"2021-02-12T09:42:46.486Z","1.1.5":"2021-02-12T20:00:21.794Z","1.2.0-beta.1":"2021-02-12T21:10:09.979Z","1.1.6":"2021-02-12T21:12:34.084Z","1.2.0-beta.2":"2021-02-12T21:13:16.408Z","1.2.0-beta.3":"2021-02-27T18:48:51.589Z","1.1.7":"2021-03-03T14:10:20.224Z","1.2.0-beta.4":"2021-03-04T15:22:43.572Z","1.2.0-beta.5":"2021-03-04T15:29:43.812Z","1.2.0":"2021-03-04T15:32:35.862Z","1.3.0-beta.1":"2021-03-05T15:55:16.554Z","1.3.0-beta.2":"2021-03-09T10:08:25.198Z","1.3.0-beta.3":"2021-03-13T15:55:15.274Z","1.3.0-beta.4":"2021-03-13T16:02:28.478Z","1.3.0":"2021-03-13T16:11:04.728Z","1.3.0-beta.5":"2021-03-13T18:46:59.141Z","1.3.0-beta.6":"2021-03-16T17:56:31.240Z","1.3.1-beta.1":"2021-03-17T19:04:10.824Z","1.3.1-beta.2":"2021-03-19T09:56:14.274Z","1.3.1-beta.3":"2021-03-19T10:04:40.754Z","1.3.1-beta.4":"2021-03-19T10:41:47.210Z","1.4.0-beta.1":"2021-03-19T11:39:10.174Z","1.4.0-beta.2":"2021-03-19T13:30:18.297Z","1.4.0-beta.3":"2021-03-22T08:26:23.813Z","0.0.0-development":"2021-03-30T18:17:31.940Z","1.4.0-beta.4":"2021-03-31T16:41:04.543Z","1.4.0-beta.5":"2021-03-31T18:10:37.963Z","1.3.1":"2021-04-04T08:05:55.200Z","1.4.0-beta.6":"2021-04-04T14:16:34.502Z","1.4.0-beta.7":"2021-04-10T10:51:29.969Z","1.4.0-beta.8":"2021-04-10T16:28:01.328Z","1.4.0":"2021-04-10T16:43:13.532Z","1.4.0-beta.9":"2021-04-12T12:03:09.664Z","1.4.0-beta.10":"2021-04-12T14:15:29.402Z","1.4.0-beta.11":"2021-04-12T14:20:43.575Z","1.4.0-beta.12":"2021-04-12T17:33:08.443Z","1.4.0-beta.13":"2021-04-12T17:42:14.657Z","1.4.1-beta.1":"2021-04-12T17:44:41.314Z","1.4.1":"2021-04-12T17:47:03.718Z","1.4.1-beta.2":"2021-04-12T17:58:38.374Z","1.4.1-beta.3":"2021-04-12T18:07:14.182Z","1.4.1-beta.4":"2021-04-12T18:26:49.663Z","1.4.1-beta.5":"2021-04-12T19:07:11.265Z","1.4.1-beta.6":"2021-04-19T12:05:05.416Z","1.5.0-beta.1":"2021-04-27T17:40:20.395Z","1.5.0-beta.2":"2021-04-29T12:46:58.148Z","1.5.0-beta.3":"2021-05-03T11:25:20.097Z","1.5.0-beta.4":"2021-05-03T11:29:24.944Z","1.5.0-beta.5":"2021-05-03T15:16:54.353Z","1.5.0-beta.6":"2021-05-03T15:23:49.739Z","1.4.2":"2021-05-03T17:32:00.956Z","1.5.0-beta.7":"2021-05-10T19:28:38.820Z","1.5.0-beta.8":"2021-05-21T11:01:07.168Z","1.4.3":"2021-05-21T11:02:39.010Z","1.5.0-beta.9":"2021-05-22T22:18:00.928Z","1.5.0-beta.10":"2021-05-24T10:26:23.014Z","1.5.0-beta.11":"2021-05-31T09:21:23.166Z","1.5.0-beta.12":"2021-06-07T22:03:16.779Z","1.4.4":"2021-06-08T08:38:29.974Z","1.4.5":"2021-06-09T07:53:37.086Z","1.4.6":"2021-06-09T07:55:34.730Z","1.4.7":"2021-06-14T08:35:20.394Z","1.5.0-beta.13":"2021-06-14T09:04:44.849Z","1.5.0-beta.14":"2021-06-14T15:45:16.087Z","1.5.0":"2021-06-14T15:56:32.667Z","1.5.1":"2021-06-14T17:03:20.248Z","1.5.2-beta.1":"2021-06-30T11:29:25.743Z","1.5.2-beta.2":"2021-06-30T11:42:23.702Z","1.5.2":"2021-06-30T11:45:47.046Z","1.5.3":"2021-07-01T08:25:58.386Z","1.5.4-beta.1":"2021-07-02T13:31:04.783Z","1.5.4":"2021-07-02T13:35:35.837Z","1.5.5-beta.1":"2021-07-03T10:04:22.154Z","1.5.5":"2021-07-03T10:05:41.783Z","1.5.5-beta.2":"2021-07-05T07:40:12.225Z","1.5.5-beta.3":"2021-07-26T17:19:24.357Z","1.5.6-beta.1":"2021-07-26T17:22:57.404Z","1.5.6":"2021-07-26T17:30:00.714Z","1.5.7":"2021-08-28T15:04:18.698Z","1.5.8-beta.1":"2021-09-17T11:03:51.773Z","1.5.8":"2021-09-17T11:10:15.914Z","1.5.9-beta.1":"2021-09-29T12:02:17.154Z","1.5.9":"2021-09-29T12:12:37.021Z","1.6.0-beta.1":"2021-10-07T14:32:40.931Z","1.6.0-beta.2":"2021-10-07T14:50:23.076Z","1.6.0":"2021-10-07T14:59:16.255Z","1.6.1":"2022-01-03T09:33:46.398Z","1.6.0-beta.3":"2022-01-27T10:47:48.184Z","1.7.0-beta.1":"2022-01-27T12:25:58.484Z","1.7.0":"2022-01-27T12:31:55.740Z","1.7.0-beta.2":"2022-01-31T17:37:01.641Z","1.7.1-beta.1":"2022-01-31T18:15:48.390Z","1.7.1":"2022-01-31T18:18:21.418Z","1.7.1-beta.2":"2022-02-05T13:24:25.069Z","1.7.2-beta.1":"2022-02-05T16:18:52.424Z","1.7.2":"2022-02-05T16:20:28.161Z","1.7.2-beta.2":"2022-06-14T10:10:25.057Z","1.7.3":"2022-06-15T10:10:21.195Z","1.8.0-beta.1":"2022-06-15T10:14:56.747Z","1.8.0":"2022-06-15T13:43:42.906Z","1.8.0-beta.2":"2022-06-16T15:57:16.167Z","1.8.1":"2022-06-16T16:01:04.348Z","1.8.0-beta.3":"2022-06-22T13:08:11.947Z","1.8.2":"2022-06-24T10:39:54.366Z"},"maintainers":[{"name":"amsbot","email":"amsbot@storyofams.com"},{"name":"bjvda","email":"bart@storyofams.com"},{"name":"doekeleeuwis","email":"dev@storyofams.com"},{"name":"mvanbever-ams","email":"mitchel@storyofams.com"}],"description":"Collection of decorators to create typed Next.js API routes, with easy request validation and transformation.","author":{"name":"Story of AMS","email":"dev@storyofams.com","url":"https://storyofams.com"},"license":"MIT","readme":"<div align=\"center\">\n  <a aria-label=\"Story of AMS logo\" href=\"https://storyofams.com/\" target=\"_blank\" align=\"center\">\n    <img src=\"https://avatars.githubusercontent.com/u/19343504\" alt=\"Story of AMS\" width=\"100\">\n  </a>\n  <h1 align=\"center\">@storyofams/next-api-decorators</h1>\n  <p align=\"center\">\n    <a aria-label=\"releases\" href=\"https://GitHub.com/storyofams/next-api-decorators/releases/\" target=\"_blank\">\n      <img src=\"https://github.com/storyofams/next-api-decorators/workflows/Release/badge.svg\">\n    </a>\n    <a aria-label=\"npm\" href=\"https://www.npmjs.com/package/@storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://img.shields.io/npm/v/@storyofams/next-api-decorators\">\n    </a>\n    <a aria-label=\"codecov\" href=\"https://codecov.io/gh/storyofams/next-api-decorators\" target=\"_blank\">\n      <img src=\"https://codecov.io/gh/storyofams/next-api-decorators/branch/master/graph/badge.svg?token=ZV0YT4HU5H\">\n    </a>\n    <a aria-label=\"stars\" href=\"https://github.com/storyofams/next-api-decorators/stargazers/\" target=\"_blank\">\n      <img src=\"https://img.shields.io/github/stars/storyofams/next-api-decorators.svg?style=social&label=Star&maxAge=86400\" />\n    </a>\n  </p>\n</div>\n\n---\n\n<div align=\"center\">\n  A collection of decorators to create typed Next.js API routes, with easy request validation and transformation.\n\n  [View docs](https://next-api-decorators.vercel.app/)\n</div>\n\n---\n\n## Basic usage\n\n```ts\n// pages/api/user.ts\nclass User {\n  // GET /api/user\n  @Get()\n  async fetchUser(@Query('id') id: string) {\n    const user = await DB.findUserById(id);\n\n    if (!user) {\n      throw new NotFoundException('User not found.');\n    }\n\n    return user;\n  }\n\n  // POST /api/user\n  @Post()\n  @HttpCode(201)\n  async createUser(@Body(ValidationPipe) body: CreateUserDto) {\n    return await DB.createUser(body.email);\n  }\n}\n\nexport default createHandler(User);\n```\n\n💡 Read more about validation [here](https://next-api-decorators.vercel.app/docs/validation)\n\n<details>\n  <summary>The code above without next-api-decorators</summary>\n\n  ```ts\n  export default async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === 'GET') {\n      const user = await DB.findUserById(req.query.id);\n      if (!user) {\n        return res.status(404).json({\n          statusCode: 404,\n          message: 'User not found'\n        })\n      }\n\n      return res.json(user);\n    } else if (req.method === 'POST') {\n      // Very primitive e-mail address validation.\n      if (!req.body.email || (req.body.email && !req.body.email.includes('@'))) {\n        return res.status(400).json({\n          statusCode: 400,\n          message: 'Invalid e-mail address.'\n        })\n      }\n\n      const user = await DB.createUser(req.body.email);\n      return res.status(201).json(user);\n    }\n\n    res.status(404).json({\n      statusCode: 404,\n      message: 'Not Found'\n    });\n  }\n  ```\n</details>\n\n---\n\n## Motivation\n\nBuilding serverless functions declaratively with classes and decorators makes dealing with Next.js API routes easier and brings order and sanity to your `/pages/api` codebase.\n\nThe structure is heavily inspired by NestJS, which is an amazing framework for a lot of use cases. On the other hand, a separate NestJS repo for your backend can also bring unneeded overhead and complexity to projects with a smaller set of backend requirements. Combining the structure of NestJS, with the ease of use of Next.js, brings the best of both worlds for the right use case.\n\nIf you are not familiar with Next.js or NestJS and want some more information (or need to be convinced), check out the article\n[Awesome Next.js API Routes with next-api-decorators](https://www.tpjnorton.com/blog/posts/awesome-next-js-api-routes-with-next-api-decorators) by [@tn12787](https://github.com/tn12787)\n\n\n## Installation\n\nVisit https://next-api-decorators.vercel.app/docs/#installation to get started.\n\n## Documentation\n\nRefer to our docs for usage topics:\n\n[Validation](https://next-api-decorators.vercel.app/docs/validation)\n\n[Route matching](https://next-api-decorators.vercel.app/docs/routing/route-matching)\n\n[Using middlewares](https://next-api-decorators.vercel.app/docs/middlewares)\n\n[Custom middlewares](https://next-api-decorators.vercel.app/docs/middlewares#custom-middleware-decorators)\n\n[Pipes](https://next-api-decorators.vercel.app/docs/pipes)\n\n[Exceptions](https://next-api-decorators.vercel.app/docs/exceptions)\n\n## Available decorators\n\n### Class decorators\n\n|                                           | Description                                                    |\n| ----------------------------------------- | -------------------------------------------------------------- |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into all routes defined in the class. |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for all the routes defined in the class. |\n\n### Method decorators\n\n|                                           | Description                                       |\n| ----------------------------------------- | ------------------------------------------------- |\n| `@Get(path?: string)`                     | Marks the method as `GET` handler.                |\n| `@Post(path?: string)`                    | Marks the method as `POST` handler.               |\n| `@Put(path?: string)`                     | Marks the method as `PUT` handler.                |\n| `@Delete(path?: string)`                  | Marks the method as `DELETE` handler.             |\n| `@Patch(path?: string)`                   | Marks the method as `PATCH` handler.             |\n| `@SetHeader(name: string, value: string)` | Sets a header name/value into the route response. |\n| `@HttpCode(code: number)`                 | Sets the http code in the route response.         |\n| `@UseMiddleware(...middlewares: Middleware[])` | Registers one or multiple middlewares for the handler. |\n\n### Parameter decorators\n\n|                         | Description                                 |\n| ----------------------- | ------------------------------------------- |\n| `@Req()`                | Gets the request object.                    |\n| `@Res()`*               | Gets the response object.                   |\n| `@Body()`               | Gets the request body.                      |\n| `@Query(key: string)`   | Gets a query string parameter value by key. |\n| `@Header(name: string)` | Gets a header value by name.                |\n| `@Param(key: string)`   | Gets a route parameter value by key.        |\n\n\\* Note that when you inject `@Res()` in a method handler you become responsible for managing the response. When doing so, you must issue some kind of response by making a call on the response object (e.g., `res.json(...)` or `res.send(...)`), or the HTTP server will hang.\n","readmeFilename":"README.md","keywords":["typescript","next.js","decorators"],"repository":{"type":"git","url":"git+https://github.com/storyofams/next-api-decorators.git"},"bugs":{"url":"https://github.com/storyofams/next-api-decorators/issues"},"homepage":"https://github.com/storyofams/next-api-decorators#readme"}