# Migrate from v5
# What's new ?
# Platform API
V6 marks a major evolution of the Ts.ED framework. A lot of work has been done on the internal Ts.ED code since v5 in order to prepare the arrival of this new version. This work was mainly oriented on the creation of an abstraction layer between the Ts.ED framework and Express.js.
The v5 introduced the Platform API and the v6 is the confirmation of this API which allows supporting Express.js (opens new window) and Koa.js (opens new window) and many more in the future.
We are glad this work resulted in the creation of the @tsed/platform-express and @tsed/platform-koa.
See also
- Template engine: Configure template engine with Platform API.
- Statics files: Configure statics files with Platform API.
- Upload files: Multer is now a part of @tsed/common.
# Schema and OpenSpec
This release finally adds support for OpenSpec 3 (opens new window) while supporting the previous version Swagger2 (opens new window). The management of OpenSpec is at the heart of the framework as is JsonSchema (opens new window).
All decorators related to the declaration of schema, routes and endpoints are now integrated in a single module @tsed/schema
(opens new window).
This module has been designed to be used independently of the Ts.ED framework.
You can therefore use it for your projects without installing the whole framework!
See also
New features are available:
WARNING
These decorators have moved from @tsed/common
/@tsed/swagger
to @tsed/schema
:
# JsonMapper
In the same idea, the convertersService code was taken out of the @tsed/common
(opens new window) module
to the new @tsed/json-mapper
(opens new window) module.
It's based on the @tsed/schema
(opens new window) module to perform the mapping of your classes
to a Plain Object JavaScript object and vice versa.
You can therefore use it for your projects without installing the whole framework!
See also
- Ignore decorator accepts a callback to define when the property should be ignored.
- serialize and deserialize function can be used in place of ConverterService .
@Converter
has been replaced in favor of JsonMapper .
# Cache
Ts.ED provide now, a unified cache manager solution based on the awesome cache-manager
(opens new window).
See our dedicated page on Cache.
# Breaking changes
# Api & Features
ServerLoader
API has been removed in favor of Platform API. See Platform API.Filter
feature has been removed in favor of Pipes.GlobalErrorHandlerMiddleware
has been removed in favor of Exception Filters.-
ConverterService
doesn't perform data validation. Validation is performed by
@tsed/ajv
package or any other validation library.
# Modules
The following modules have been removed:
@tsed/testing
: Use PlatformTest from@tsed/common
.@tsed/multipartfiles
: Use MultipartFile from@tsed/common
.ts-express-decorators
: Use@tsed/common
.
# Decorators
The following decorators have been removed:
# @tsed/di
@OverrideService
: Use OverrideProvider
# @tsed/common
@ServerSettings
: Use Configuration .@ExpressApplication
: Use PlatformApplication instead.@ExpressRouter
: Use PlatformRouter instead.@ResponseView
: Use View decorator instead.@Filter
: Filter feature has been removed.@MiddlewareError
: Use Middleware .@Converter
: @Converter is replaced by JsonMapper from@tsed/json-mapper
.PropertyDeserialize
andPropertySerialize
have been removed and replaced by OnDeserialize and OnSerialize from@tsed/json-mapper
.
# @tsed/typeorm
@EntityRepository
: Use EntityRepository fromtypeorm
directly.
# @tsed/swagger
Import the following decorators from @tsed/schema
:
@BaseParameter
has been removed.@Operation
has been removed.@Responses
has been removed.@ReturnsArray
has been removed. Use Returns from@tsed/schema
.
# Classes
# @tsed/common
- Classes like
ArrayConverter
,SetConverter
, etc. are replaced by their equivalents ArrayMapper , SetMapper , etc. These classes cannot be injected to another provider.
# Migration guide
# ServerLoader to Platform API
All changes related to Platform API and how to migrate the Server on this new API, are described on a dedicated page. We encourage you to browse the entire page to migrate from v4/v5 to v6.
See our Platform API documentation page.
# Inject service in the Server
With the ServerLoader
API in v4/5, injecting a provider can be done as follows:
import {ServerLoader, ServerSettings} from "@tsed/common";
import {MyService} from "./services/MyService";
@ServerLoader({})
export class Server extends ServerLoader {
$beforeRoutesInit() {
const myService = this.injector.get<MyService>(MyService);
myService.getSomething();
}
}
2
3
4
5
6
7
8
9
10
11
Now Platform API, the Server class is considered as a Provider . It means that you can use decorator like Constant and Inject to get any configuration, provider or service from the DI registry.
import {Configuration} from "@tsed/common";
import {Inject} from "@tsed/di";
import {MyService} from "./services/MyService";
@Configuration({})
export class Server {
@Inject()
myService: MyService;
$beforeRoutesInit() {
this.myService.getSomething();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# SendResponseMiddleware & PlatformResponseMiddleware to ResponseFilter
Breaking changes
- SendResponseMiddleware has been removed.
- PlatformResponseMiddleware has been removed.
To reach cross compatibility and performance with Express.js and Koa.js, the SendResponseMiddleware (aka PlatformResponseMiddleware in latest v5 version) has been removed.
Now, when a request is sent to the server all middlewares added in the Server, Controller or Endpoint with decorators will be called while a response isn't sent by one of the handlers/middlewares in the stack.
For each executed endpoints and middlewares, Platform API stores the return value to the Context . We have two scenarios:
- If data is stored in the Context object, the response will be immediately sent to your consumer after the UseAfterEach middleware (if present).
- If no data is stored in the Context object, the call sequence middlewares continue to the next endpoint (if present) or to the UseAfter then Global middlewares until data is returned by a handler.
By removing this middleware, it isn't possible for the v5 application to override the middleware and change the response format before sending it to the consumer.
The Response Filter, implemented in v6.1.0, allows this possibility again but in a more elegant way by using the @ResponseFilter
decorator and a class.
WARNING
The wrapper won't be documented in your generated swagger.json
!
TIP
See all possibilities of this new feature on its dedicated page Response Filter.
# GlobalErrorHandler to Exception Filter
Breaking changes
- CustomGlobalErrorHandlerMiddleware has been removed.
- Default Exception Filter returns a Json object to your consumer.
To fix that, remove the line where you add you custom middleware in the server:
class Server {
$afterRoutesInit() {
this.app.use(CustomGlobalErrorHandlerMiddleware); // remove this
}
}
2
3
4
5
TIP
To migrate your CustomGlobalErrorHandlerMiddleware
to create an exception filter, see our Exception Filter documentation page to know what is the appropriate implementation
for your usecase.
Exception Filter uses the Catch decorator to catch a specific instance error. For example, if you want to catch an Http exception, you have to provide the generic Exception class to the decorator as follows:
import {PlatformContext, ResponseErrorObject} from "@tsed/common";
import {Catch, ExceptionFilterMethods} from "@tsed/platform-exceptions";
import {Exception} from "@tsed/exceptions";
@Catch(Exception)
export class HttpExceptionFilter implements ExceptionFilterMethods {
catch(exception: Exception, ctx: PlatformContext) {
const {response, logger} = ctx;
const error = this.mapError(exception);
const headers = this.getHeaders(exception);
logger.error({
error
});
response.setHeaders(headers).status(error.status).body(error);
}
mapError(error: any) {
return {
name: error.origin?.name || error.name,
message: error.message,
status: error.status || 500,
errors: this.getErrors(error)
};
}
protected getErrors(error: any) {
return [error, error.origin].filter(Boolean).reduce((errs, {errors}: ResponseErrorObject) => {
return [...errs, ...(errors || [])];
}, []);
}
protected getHeaders(error: any) {
return [error, error.origin].filter(Boolean).reduce((obj, {headers}: ResponseErrorObject) => {
return {
...obj,
...(headers || {})
};
}, {});
}
}
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
# Converter to JsonMapper
The @tsed/json-mapper
package is now responsible to map a plain object to a model and a model to a plain object.
It provides two functions
serialize
and
deserialize
to transform objects depending on which operation you want to perform.
It uses all decorators from @tsed/schema
package and TypeScript metadata to work.
Breaking changes
- The
@Converter
decorator has been removed in favor of JsonMapper decorator. - Classes like
ArrayConverter
,SetConverter
, etc. are replaced by their equivalents Types mapper: ArrayMapper , SetMapper , etc. - Type mapper classes are no longer injectable services.
- ConverterService is always available and can be injected to another provider, but now, ConverterService doesn't perform data validation. Validation is performed by
@tsed/ajv
package or any other validation library. PropertyDeserialize
andPropertySerialize
have been removed and replaced by OnDeserialize and OnSerialize .- Methods signatures of Type mapper (like ArrayConverter) have changed.
Here is the ArrayConverter
as implemented in v5:
import {Converter, IConverter, IDeserializer, ISerializer} from "@tsed/common";
@Converter(Array)
export class ArrayConverter implements IConverter {
deserialize<T>(data: any, target: any, baseType: T, deserializer: IDeserializer): T[] {
return [].concat(data).map((item) => deserializer!(item, baseType));
}
serialize(data: any[], serializer: ISerializer) {
return [].concat(data as any).map((item) => serializer(item));
}
}
2
3
4
5
6
7
8
9
10
11
12
Now, The new ArrayMapper as implemented in v6:
import {JsonMapper, JsonMapperCtx, JsonMapperMethods} from "@tsed/json-mapper";
@JsonMapper(Array)
export class ArrayMapper implements JsonMapperMethods {
deserialize<T = any>(data: any, options: JsonMapperCtx): T[] {
return [].concat(data).map((item) => options.next(item));
}
serialize(data: any[], options: JsonMapperCtx): any {
return [].concat(data as any).map((item) => options.next(item));
}
}
2
3
4
5
6
7
8
9
10
11
12
To help you migrate your custom mapper, here is a small table of equivalent points between v5 and v6:
V5 | V6 | Description |
---|---|---|
Converter | JsonMapper | The decorator has the same behavior. You can use JsonMapper to override an existing mapper. |
deserializer/serializer | options.next(item) | Call the next function to deserialize/serialize object. |
target/baseType | JsonMapperCtx.type/JsonMapperCtx.collectionType | The types of the object and the collection. |
See also
See our JsonMapper documentation page for details on Type mapper.
# Any with BodyParams
Use any
as type for a body parameter, will be translated as type Object
by typescript.
It means, if you use @tsed/ajv
, now, the validation will fail if you send a different type as expected in the payload.
import {BodyParams} from "@tsed/platform-params";
import {Post} from "@tsed/schema";
import {Controller} from "@tsed/di";
import {Any} from "@tsed/schema";
@Controller("/any")
export class AnyCtrl {
@Post()
updatePayload1(@BodyParams() payload: any): any {
// accept only Object
// breaking change with v5
console.log("payload", payload);
return payload;
}
@Post()
updatePayload2(@BodyParams() @Any() payload: any): any {
// accept all types
console.log("payload", payload);
return payload;
}
@Post()
updatePayload3(@BodyParams() payload: any[]): any {
// accept array of any types
console.log("payload", payload);
return payload;
}
}
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
Add Any decorator to fix the issue.
# Enum with BodyParams
Use an enum as default value for a body parameter (or query parameter), will be translated as type Object
by typescript.
It means, if you use @tsed/ajv
, now, the validation will fail if you send a different type as expected in the payload.
import {QueryParams} from "@tsed/platform-params";
import {Post} from "@tsed/schema";
import {Controller} from "@tsed/di";
import {Enum} from "@tsed/schema";
export enum SearchType {
PARTIAL = "partial",
EXTENDED = "extended"
}
@Controller("/enums")
export class AnyCtrl {
// BREAKING CHANGE with v5
@Post()
updatePayload1(@QueryParams("type") type = SearchType.PARTIAL): any {
// type = SearchType.PARTIAL will considered by typescript as any => Object
}
// WORKS
@Post()
updatePayload2(@QueryParams("type") type: SearchType = SearchType.PARTIAL): any {}
// Add validation
@Post()
updatePayload3(@QueryParams("type") @Enum(SearchType) type: SearchType = SearchType.PARTIAL): any {}
}
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
# Status decorator
Status
decorator from @tsed/schema
is different from @tsed/common
:
# Returns decorator
Returns
decorator from @tsed/schema
is different from @tsed/common
:
# ReturnsArray decorator
ReturnsArray
is deprecated and will be removed in v7. You have to use
Returns
.
Last Updated: 2/11/2022, 7:27:21 PM
Other topics
- Session & cookies
- Passport.js
- Keycloak
- Prisma
- TypeORM
- MikroORM
- Mongoose
- GraphQL
- Socket.io
- Swagger
- AJV
- Multer
- Serve static files
- Templating
- Serverless HTTP
- Seq
- OIDC
- Stripe
- Agenda
- Terminus
- Serverless
- IORedis
- Controllers
- Providers
- Model
- JsonMapper
- Middlewares
- Pipes
- Interceptors
- Authentication
- Hooks
- Exceptions
- Throw HTTP Exceptions
- Cache
- Command
- Response Filter
- Injection scopes
- Custom providers
- Lazy-loading provider
- Custom endpoint decorator
- Testing
- Customize 404