Advanced Content

If you missed the first two articles, you are strongly advised to read them first. Chapter-1 and Chapter-2

In this chapter, we will be implementing error handling and input validation.

Validate Input

Let’s first import the necessary components.

1
2
3
4
5
6
import {
Valid,
Min,
Max,
NotNull,
} from '@t2ee/validation';

We will be implementing a TodoItem class which specify how we should validate inputs.

1
2
3
4
5
6
7
8
class TodoItem {
id: number;
@NotNull
@Min(1)
@Max(10)
name: string;
}

Now we have a form that takes two fields, id and name. id can be null, and name should be a string with 1-10 characters.

To make it automatically validate input body, all we have to do is two modifications on method declarations, following is an example on postItem.

1
async postItem(@Valid @Body body: TodoItem, @PathParam('id') id)

First, we need to add @valid decorator to tell our validator that this parameter should be validated, and change its declaration type to the class we implemented above. Thus, all those rules will be validated against request body.

Apply the same to putItem. Now we have all our input validated;

Error Handling

The one problem with this api service is that it is not really RESTful, all errors are not correctly returned to the user.

To achive so, we will implement a configuration class to implement error handling.

First, let’s create a file config.ts, and import it in index.ts, something like import './config'. This makes sure configurations are applied.

Now, import all components that we are gonna use.

1
2
3
4
5
6
7
8
9
10
11
12
13
import {
Configuration,
Bean,
} from '@t2ee/core';
import {
NotFoundHandler,
ErrorHandler,
Request,
Response,
} from '@t2ee/vader';
import {
ValidationError,
} from '@t2ee/validation';

To provide autowirable objects globally, we need to have a configuration class.

1
2
3
@Configuration
class Config {
}

Let’s first handle 404 properly.

Add following method to class Config.

1
2
3
4
5
6
7
8
9
10
11
12
@Bean('NotFoundHandler')
notFoundHandler(): NotFoundHandler {
return {
async handle(req: Request, data: void): Promise<Response> {
const res = new Response();
res.body = {
error: 'not found',
}
return res;
}
}
}

@Bean means this is injected as an instance not a class. Passing a string to it, makes us/vader access it by name (since NotFoundHandler is an interface, which is psuedo, that is not generated in js).

In the handler, we make it returnning json response, this makes it easier to handle on client side.

Notice after we added validation, the server responds a internal error to the client if wrong data were requested. It is because there is no handler for this type of error.

Add following to Config.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Bean('ErrorHandler')
errorHandler(): ErrorHandler {
return {
async handle(req: Request, e: Error): Promise<Response> {
let error = 'internal error';
if (e instanceof ValidationError) {
error = 'request error';
}
const res = new Response();
res.body = {
error,
}
return res;
}
}
}

Put it together

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import {
Configuration,
Bean,
} from '@t2ee/core';
import {
NotFoundHandler,
ErrorHandler,
Request,
Response,
} from '@t2ee/vader';
import {
ValidationError,
} from '@t2ee/validation';
@Configuration
class Config {
@Bean('NotFoundHandler')
notFoundHandler(): NotFoundHandler {
return {
async handle(req: Request, data: void): Promise<Response> {
const res = new Response();
res.body = {
error: 'not found',
}
return res;
}
}
}
@Bean('ErrorHandler')
errorHandler(): ErrorHandler {
return {
async handle(req: Request, e: Error): Promise<Response> {
let error = 'internal error';
if (e instanceof ValidationError) {
error = 'request error';
}
const res = new Response();
res.body = {
error,
}
return res;
}
}
}
}

Lastly

This is the end of this project. Again, you can access all source codes for each chapter at https://github.com/t2ee/tutorial-series-1.

I hope you know a bit more of @t2ee components by now. In the next series, we will be learning how to customize them.

See you soon!