From d4319a6523fed1eba1fefd088239b1d2e6a1591f Mon Sep 17 00:00:00 2001 From: Evan Shortiss Date: Sun, 25 Aug 2019 21:52:53 +0100 Subject: [PATCH 1/2] BREAKING CHANGE: addresses typings issues for strict mode --- CHANGELOG.md | 7 +++++ README.md | 9 +++--- example/typescript/route.ts | 30 +++++++++++++++---- example/typescript/server.ts | 2 +- express-joi-validation.d.ts | 58 +++++++++++++++++++++++++++++------- package.json | 3 +- tsconfig.json | 9 ------ 7 files changed, 85 insertions(+), 33 deletions(-) delete mode 100644 tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index e48c730..6794232 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ ## CHANGELOG Date format is DD/MM/YYYY +## 3.0.0 (25/08/2019) +* Removed `fields`, `originalQuery`, `originalHeaders`, `originalBody`, +`originalParams`, and `originalFields` from `ValidatedRequest`. This simplifies +usage with TypeScript's *strict* mode. +* Added `ValidatedRequestWithRawInputsAndFields`. This is the same as +`ValidatedRequest` from versions 2.x. + ## 2.0.1 (22/08/2019) * Fixed compilation issue with TypeScript example when `strict` compiler flag is `true`. * Updated test script to include building TypeScript example diff --git a/README.md b/README.md index 4d4631f..70c56fa 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,8 @@ app.get('/orders', validator.query(querySchema), (req, res) => { ## Usage (TypeScript) -For TypeScript a helper `ValidatedRequest` type is provided. This extends the +For TypeScript a helper `ValidatedRequest` and +`ValidatedRequestWithRawInputsAndFields` type is provided. This extends the `express.Request` type and allows you to pass a schema using generics to ensure type safety in your handler function. @@ -117,11 +118,9 @@ interface HelloRequestSchema extends ValidatedRequestSchema { app.get( '/hello', validator.query(querySchema), - (req, res) => { + (req: ValidatedRequest, res) => { // Woohoo, type safety and intellisense for req.query! - const vreq = req as ValidatedRequest - - res.end(`Hello ${vreq.query.name}!`) + res.end(`Hello ${req.query.name}!`) } ) ``` diff --git a/example/typescript/route.ts b/example/typescript/route.ts index 95402d5..d71bc33 100644 --- a/example/typescript/route.ts +++ b/example/typescript/route.ts @@ -1,6 +1,8 @@ import * as Joi from '@hapi/joi' +import formidable from 'express-formidable' import { ValidatedRequest, + ValidatedRequestWithRawInputsAndFields, ValidatedRequestSchema, createValidator, ContainerTypes @@ -10,18 +12,34 @@ import 'joi-extract-type' const route = Router() const validator = createValidator() -const querySchema = Joi.object({ +const schema = Joi.object({ name: Joi.string().required() }) -interface HelloRequestSchema extends ValidatedRequestSchema { - [ContainerTypes.Query]: Joi.extractType +interface HelloGetRequestSchema extends ValidatedRequestSchema { + [ContainerTypes.Query]: Joi.extractType } -route.get('/hello', validator.query(querySchema), (req, res) => { - const vreq = req as ValidatedRequest +interface HelloPostRequestSchema extends ValidatedRequestSchema { + [ContainerTypes.Fields]: Joi.extractType +} + +// curl http://localhost:3030/hello/?name=express +route.get( + '/', + validator.query(schema), + (req: ValidatedRequest, res) => { + res.end(`Hello ${req.query.name}`) + } +) + +// curl -X POST -F 'name=express' http://localhost:3030/hello +route.post('/', formidable(), validator.fields(schema), (req, res) => { + const vreq = req as ValidatedRequestWithRawInputsAndFields< + HelloPostRequestSchema + > - res.end(`Hello ${vreq.query.name}`) + res.end(`Hello ${vreq.fields.name}`) }) export default route diff --git a/example/typescript/server.ts b/example/typescript/server.ts index 1f017d7..2652871 100644 --- a/example/typescript/server.ts +++ b/example/typescript/server.ts @@ -53,6 +53,6 @@ app.listen(port, (err: any) => { `Try accessing http://localhost:${port}/ping or http://localhost:${port}/hello?name=dean to get some data.\n` ) console.log( - `Now try access hhttp://localhost:${port}/hello. You should get an error complaining that your querystring is invalid.` + `Now try access http://localhost:${port}/hello. You should get an error complaining that your querystring is invalid.` ) }) diff --git a/express-joi-validation.d.ts b/express-joi-validation.d.ts index 52084ac..59a3cb0 100644 --- a/express-joi-validation.d.ts +++ b/express-joi-validation.d.ts @@ -1,12 +1,12 @@ -import * as Joi from '@hapi/joi'; +import * as Joi from '@hapi/joi' import * as express from 'express' -import { IncomingHttpHeaders } from 'http'; +import { IncomingHttpHeaders } from 'http' /** * Creates an instance of this module that can be used to generate middleware * @param cfg */ -export function createValidator (cfg? : ExpressJoiConfig): ExpressJoiInstance +export function createValidator(cfg?: ExpressJoiConfig): ExpressJoiInstance /** * These are the named properties on an express.Request that this module can @@ -40,7 +40,25 @@ export type ValidatedRequestSchema = Record * *req.body* and others are strongly typed using your * *ValidatedRequestSchema* */ -export interface ValidatedRequest extends express.Request { +export interface ValidatedRequest + extends express.Request { + body: T[ContainerTypes.Body] + query: T[ContainerTypes.Query] + headers: T[ContainerTypes.Headers] + params: T[ContainerTypes.Params] +} + +/** + * Use this in conjunction with *ValidatedRequestSchema* instead of + * express.Request for route handlers. This ensures *req.query*, + * *req.body* and others are strongly typed using your *ValidatedRequestSchema* + * + * This will also allow you to access the original body, params, etc. as they + * were before validation. + */ +export interface ValidatedRequestWithRawInputsAndFields< + T extends ValidatedRequestSchema +> extends express.Request { body: T[ContainerTypes.Body] query: T[ContainerTypes.Query] headers: T[ContainerTypes.Headers] @@ -54,7 +72,7 @@ export interface ValidatedRequest extends expr } /** - * Configuration options supportef by *createValidator(config)* + * Configuration options supported by *createValidator(config)* */ export interface ExpressJoiConfig { joi?: typeof Joi @@ -76,10 +94,28 @@ export interface ExpressJoiContainerConfig { * calling *createValidator* */ export interface ExpressJoiInstance { - body (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler - query (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler - params (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler - headers (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler - fields (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler - response (schema: Joi.Schema, cfg?: ExpressJoiContainerConfig): express.RequestHandler + body( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler + query( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler + params( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler + headers( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler + fields( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler + response( + schema: Joi.Schema, + cfg?: ExpressJoiContainerConfig + ): express.RequestHandler } diff --git a/package.json b/package.json index bf55117..db3bd31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-joi-validation", - "version": "2.0.1", + "version": "3.0.0", "description": "validate express application inputs and parameters using joi", "main": "express-joi-validation.js", "scripts": { @@ -38,6 +38,7 @@ "devDependencies": { "@hapi/joi": "~15.0.3", "@types/express": "~4.0.39", + "@types/express-formidable": "~1.0.4", "@types/hapi__joi": "~15.0.2", "@types/node": "^6.0.117", "body-parser": "~1.18.3", diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 9ed303d..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "noImplicitAny": true, - "removeComments": true, - "preserveConstEnums": true, - "sourceMap": true - } -} From 22250497f663c78e12bc55dc6ef7c04d0d109cbd Mon Sep 17 00:00:00 2001 From: Evan Shortiss Date: Fri, 30 Aug 2019 20:34:51 +0100 Subject: [PATCH 2/2] chore: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6794232..9a44843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## CHANGELOG Date format is DD/MM/YYYY -## 3.0.0 (25/08/2019) +## 3.0.0 (30/08/2019) * Removed `fields`, `originalQuery`, `originalHeaders`, `originalBody`, `originalParams`, and `originalFields` from `ValidatedRequest`. This simplifies usage with TypeScript's *strict* mode.