Skip to content

Commit

Permalink
Merge 45729f0 into 84a90c5
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Oct 12, 2020
2 parents 84a90c5 + 45729f0 commit a5ef5a6
Show file tree
Hide file tree
Showing 64 changed files with 2,016 additions and 153 deletions.
3 changes: 1 addition & 2 deletions docs/.vuepress/config.js
Expand Up @@ -111,8 +111,7 @@ module.exports = {
collapsable: true,
children: [
"/getting-started/migration-from-v5",
"/getting-started/migrate-from-express",
"/getting-started/migrate-from-koa"
"/getting-started/migrate-from-express"
]
},
{
Expand Down
93 changes: 81 additions & 12 deletions docs/docs/controllers.md
Expand Up @@ -144,7 +144,7 @@ class QueryController {

### Headers

@@HeaderParams@@ decorator provides you quick access to the `Express.request.get()`
@@HeaderParams@@ decorator provides you quick access to the `Express.request.get()`:

<<< @/docs/docs/snippets/controllers/request-headers.ts

Expand All @@ -170,6 +170,26 @@ Here is an example:

See our dedicated page on [PlatformContext](/docs/request-context.md) for more details.

#### Validation

Ts.ED support the data input validation with the decorators provided by `@tsed/schema`.

Example:

<<< @/docs/docs/snippets/controllers/request-input-validation.ts

::: warning
Validation require the `@tsed/ajv` plugins to work.

```sh
npm install --save @tsed/ajv
```
:::

**Supported decorators:**

<ApiList query="module === '@tsed/schema' && status.includes('decorator') && status.includes('schema') && !status.includes('operation') && !['Property'].includes(symbolName)" />

## Response

### Decorators
Expand All @@ -195,6 +215,42 @@ You can set the response header with the @@Header@@ decorator:

<<< @/docs/docs/snippets/controllers/response-headers.ts

### Generics

One of the new usage allowed by the @@Returns@@ is the support of the Generics from TypeScript.

This feature is basically there to meet the need to generate correct Swagger documentation when using generic templates.

For example, you want to return a generic `Document` payload which contains a data (Product) and links to allow a commuter to discover your endpoints linked to this data.

With @@Returns@@ you can document correctly your endpoint to reflect the correct model:

<Tabs class="-code">
<Tab label="MyController.ts">

<<< @/docs/docs/snippets/controllers/response-generics-controller.ts

</Tab>
<Tab label="Document.ts">

<<< @/docs/docs/snippets/controllers/response-generics-document.ts

</Tab>
<Tab label="Product.ts">

<<< @/docs/docs/snippets/controllers/response-generics-product.ts

</Tab>
<Tab label="CodeSandbox">
<iframe src="https://codesandbox.io/embed/laughing-kepler-ripfl?fontsize=14&hidenavigation=1&theme=dark"
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
title="tsed-swagger-example"
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"></iframe>
</Tab>
</Tabs>

### Throw exceptions

You can use [@tsed/exceptions](/docs/exceptions.md) or similar module to throw an http exception.
Expand All @@ -214,25 +270,39 @@ This example will produce a response with status code 400 and "Not a number" mes
See our guide on [HttpExceptions to throw customer HttpExceptions](/tutorials/throw-http-exceptions.md)
:::

## Inject request, response and next
## Inject Request and Response

You can use a decorator to inject `Express.Request`, `Express.Response` and
`Express.NextFunction` services instead of the classic call provided by Express API.
You can use a decorator to inject the Request in order to retrieve information from the request that you cannot get through decorators.
In the same way you can inject the Response instance in order to modify some of its information.

- @@Req@@
- @@Res@@
- @@Next@@
This is not recommended, however, because your will potentially be specific to the platform you are using (Express.js, Koa.js, etc ...)

Here an example to use these decorators:
You can with the Req and Request decorators retrieve the originals request and response as follows:

<<< @/docs/docs/snippets/controllers/raw-req-res-next.ts
<<< @/docs/docs/snippets/controllers/inject-req-res-target.ts

It's also possible to inject the high level PlatformRequest and PlatformResponse:

<<< @/docs/docs/snippets/controllers/inject-req-res-target.ts

Finally, it is also possible to retrieve the request and response in Node.js version:

<<< @/docs/docs/snippets/controllers/inject-req-res-node.ts

## Inject next

Use @@Next@@ decorator isn't recommended because Ts.ED use Promise/Observable to return a response, but something it's required to get next function
to chain middlewares.

<<< @/docs/docs/snippets/controllers/inject-next.ts

## Inject router

Each controller has a @@PlatformRouter@@ which wrap the original router from [Express.Router](http://expressjs.com/en/guide/routing.html
You can inject @@PlatformRouter@@ in your controller to add anything related to the Router itself.
or KoaRouter.
You can inject @@PlatformRouter@@ in your controller to add anything related to the current Router controller.

<<< @/docs/docs/snippets/controllers/handle-router-controller.ts
<<< @/docs/docs/snippets/controllers/inject-router.ts

::: warning
All of these routes added by this way won't be discovered by Ts.ED to produce Swagger documentation.
Expand Down Expand Up @@ -278,7 +348,6 @@ The following decorators lets you add custom middleware on a method or on contro

<<< @/docs/docs/snippets/controllers/middlewares.ts


### Middleware call sequence

When a request is sent to the server all middlewares added on the Server, Controller or Endpoint
Expand Down
128 changes: 106 additions & 22 deletions docs/docs/model.md
Expand Up @@ -49,7 +49,7 @@ The schema generated by Ts.ED lists only properties decorated by at least one de
the `_id` won't be displayed in the JsonSchema. It's very important to understand that **TypeScript** only generates
metadata on properties with at least of theses decorators:

<ApiList query="module === '@tsed/schema' && status.includes('decorator') && status.includes('model')" />
<ApiList query="module === '@tsed/schema' && status.includes('decorator') && status.includes('schema')" />

:::

Expand Down Expand Up @@ -316,6 +316,92 @@ Or by using @@getJsonSchema@@ in combination with @@AdditionalProperty@@ as foll
</Tab>
</Tabs>

## Generics
### Declaring a generic model

Sometime, it might be useful to use generic models. TypeScript doesn't store the generic type in the metadata. This is why we need to
declare explicitly the generic models with the decorators.

One of the generic's usage, can be a paginated list. With Returns decorator it's now possible to declare generic type and generate the appropriate OpenSpec documentation.

Starting with the pagination model, by using @@Generics@@ and @@CollectionOf@@:

<<< @/docs/docs/snippets/model/generics-pagination.ts

Now, we need a model to be used with the generic Pagination model:

<<< @/docs/docs/snippets/model/generics-product.ts

Finally, we can use our models on a method as following:

<Tabs class="-code">
<Tab label="MyController.ts">

<<< @/docs/docs/snippets/model/generics-controller1.ts

</Tab>
<Tab label="OpenSpec 2">

<<< @/docs/docs/snippets/model/generics-controller1-os2.json

</Tab>
<Tab label="OpenSpec 3">

<<< @/docs/docs/snippets/model/generics-controller1-os3.json

</Tab>
</Tabs>

### Declaring a nested generics models

It's also possible to declare a nested generics models in order to have this type `Pagination<Submission<Product>>`:

<Tabs class="-code">
<Tab label="MyController.ts">

```typescript
import {Generics, Property, Returns} from "@tsed/schema";
import {Post} from "@tsed/common";

class MyController {
@Post("/")
@Returns(200, Pagination).Of(Submission).Nested(Product).Description("description")
async method(): Promise<Pagination<Submission<Product>> | null> {
return null;
}
}
```

</Tab>
<Tab label="Submission.ts">

<<< @/docs/docs/snippets/model/generics-submission.ts

</Tab>
<Tab label="Pagination.ts">

<<< @/docs/docs/snippets/model/generics-pagination.ts

</Tab>
<Tab label="Product.ts">

<<< @/docs/docs/snippets/model/generics-product.ts

</Tab>
<Tab label="OpenSpec 2">

<<< @/docs/docs/snippets/model/generics-controller2-os2.json

</Tab>
<Tab label="OpenSpec 3">

<<< @/docs/docs/snippets/model/generics-controller2-os3.json

</Tab>
</Tabs>



## Annotations

JSON Schema includes a few keywords and Ts.ED provide also theses corresponding decorators like @@Title@@, @@Description@@, @@Default@@, @@Examples@@ that aren’t strictly used for validation, but are used to describe parts of a schema.
Expand Down Expand Up @@ -366,12 +452,12 @@ an inline schema:
<Tabs class="-code">
<Tab label="Model">

<<< @/docs/docs/snippets/model/schema.ts
<<< @/docs/docs/snippets/model/get-spec-generics-controller1.ts

</Tab>
<Tab label="Json schema">

<<< @/docs/docs/snippets/model/schema.json
<<< @/docs/docs/snippets/model/generics-controller1-os3.json

</Tab>
</Tabs>
Expand All @@ -391,25 +477,23 @@ This is possible by using @@getJsonSchema@@. Here a small example:

<<< @/docs/docs/snippets/model/jsonschema.json

```json
{
"type": "object",
"properties": {
"firstName": {
"type": "string",
"minLength": 3
},
"lastName": {
"type": "string",
"minLength": 3
}
},
"required": [
"firstName",
"lastName"
]
}
```
</Tab>
</Tabs>

## Get OpenSpec

In some cases, it may be useful to retrieve the OpenSpec from a Controller to generate the Swagger OpenSpec.
This is possible by using @@getSpec@@. Here a small example:

<Tabs class="-code">
<Tab label="MyController">

<<< @/docs/docs/snippets/model/get-spec-generics-controller1.ts

</Tab>
<Tab label="OpenSpec">

<<< @/docs/docs/snippets/model/generics-controller1-os3.json

</Tab>
</Tabs>
Expand Down
@@ -1,19 +1,20 @@
import {Controller, Get, Next, Req, Res} from "@tsed/common";
import * as Express from "express";
import {promisify} from "util";

@Controller("/calendars")
export class CalendarCtrl {
@Get("/:id")
get(
async get(
@Req() request: Express.Request,
@Res() response: Express.Response,
@Next() next: Express.NextFunction
): void {
) {
setTimeout(() => {
response
.status(200)
.send({id: request.params.id, name: "test"});
next();
myExpressMiddleware(request, response, next)
});

// But it's also possible to do this
await promisify(myExpressMiddleware)(request, response)
}
}
14 changes: 14 additions & 0 deletions docs/docs/snippets/controllers/inject-req-res-node.ts
@@ -0,0 +1,14 @@
import {Controller, Get, Req, Res} from "@tsed/common";
import {IncomingMessage, ServerResponse} from "http";

@Controller("/calendars")
export class CalendarCtrl {
@Get("/:id")
get(
@Req() request: IncomingMessage,
@Res() response: ServerResponse
): void {
console.log(request); // IncomingMessage
console.log(response); // ServerResponse
}
}
17 changes: 17 additions & 0 deletions docs/docs/snippets/controllers/inject-req-res-platform.ts
@@ -0,0 +1,17 @@
import {Controller, Get, Req, Res, PlatformRequest, PlatformResponse} from "@tsed/common";

@Controller("/calendars")
export class CalendarCtrl {
@Get("/:id")
get(
@Req() request: PlatformRequest,
@Res() response: PlatformResponse
): void {
console.log(request) // PlatformRequest
console.log(response) // PlatformResponse

response
.status(200)
.body({id: request.params.id, name: "test"});
}
}
16 changes: 16 additions & 0 deletions docs/docs/snippets/controllers/inject-req-res-target.ts
@@ -0,0 +1,16 @@
import {Controller, Get, Req, Res} from "@tsed/common";

@Controller("/calendars")
export class CalendarCtrl {
@Get("/:id")
get(
@Req() request: any,
@Res() response: any
): void {
console.log(request) // Express.Request or Koa.Request
console.log(response) // Express.Request or Koa.Request
response
.status(200)
.send({id: request.params.id, name: "test"});
}
}
Expand Up @@ -4,8 +4,11 @@ import {Controller, PlatformRouter} from "@tsed/common";
export class CalendarCtrl {
constructor(router: PlatformRouter) {
router.get("/", this.myMethod);
// GET raw router (Express.Router)

// GET raw router (Express.Router or Koa.Router)
console.log(router.callback());
console.log(router.raw);
console.log(router.getRouter());
}

myMethod(req: any, res: any, next: any) {
Expand Down

0 comments on commit a5ef5a6

Please sign in to comment.