In Part One, we set up our TypeScript environment, created a “Hello World!” application with Express, and compiled it with the TypeScript compiler. In this section, we will add more routes and validate requests. For validation, we will leverage an experimental TypeScript feature: decorators.

In order to enable this feature, we need to modify our tsconfig.json file to add two lines like this:

The experimentalDecorators option enables decorators (which are an experimental feature, disabled by default) and emitDecoratorMetadata will add run-time type information to our JavaScript code which will power our validations. We also need to install a few dependencies to make this feature work:

The technical details of decorators aren’t complicated. They’re just functions that run “around” where they are declared, whether that’s on an entire class, a property, a method, or a method parameter.

Our use of decorators will be around validating requests our API receives in a declarative way. Here’s what it will look like in use:

classCreatePostRequest{@Length(1,250)body: string='';}

We’re declaring the request to contain an object with one property (body) that is a string between 1 and 250 characters.

In order to make this easy to use, we’re going to create an Express middleware in src/validation.ts.

// src/validation.tsimport*asexpressfrom'express';import{deserialize,JsonProperty}from'json-typescript-mapper';import{Validator}from"class-validator";import{ValidationError}from'class-validator';// Because all type information is erased in the compiled// JavaScript, we can use this clever structural-typing// work-around enabled by TypeScript to pass in a class// to our middleware.typeConstructor<T>={new():T};// This function returns a middleware which validates that the// request's JSON body conforms to the passed-in type.exportfunctionvalidate<T>(type: Constructor<T>):express.RequestHandler{letvalidator=newValidator();return(req,res,next)=>{letinput=deserialize(type,req.body);leterrors=validator.validateSync(input);if(errors.length>0){next(errors);}else{req.body=input;next();}}}// This middleware handles the case where our validation// middleware says the request failed validation. We return// those errors to the client here.exportfunctionvalidationError(err: Error,req,res,next){if(errinstanceofArray&&err[0]instanceofValidationError){res.status(400).json({errors: err}).end();}else{next(err);}}

Subscribe to my mailing list to keep learning as I write!

We’ve really created two middlewares here: validate and validationError. Together, we will combine these two to automatically handle validation for us. We can use the new validation middleware like this back in app.ts

You may have noticed in this post we didn’t litter all our code with a bunch of type names. TypeScript has type inference and structural typing. Type inference means the compiler can infer which types we mean even if they aren’t specified directly in the code. This convenience saves a lot of typing and will make your TypeScript code look a lot like JavaScript, if that’s what you want. You can specify the type names when you think it helps with readability. Structural typing means TypeScript can inspect the structure of an object and use that structure in place of a type name. For example: