Comienzo
De lo que hablaremos
Referencia pertinente de API: Controller, @Route, @Get, @Path, @Query, @Post, @Body, y @SuccessResponse.
NOTA COMPATIVA
Esta guía tiene como objetivo express y asume tsoa-next's actual política de apoyo: Node.js 22 o más nuevo. Verificamos el soporte en los LTS anteriores, LTS actual y Node vSiguiente en CI. Los ejemplos que figuran a continuación son los siguientes: npm, pnpm, y yarn variantes donde el comando difiere.
Iniciando nuestro proyecto
# Create a new folder for our project
mkdir tsoa-project
cd tsoa-project
# Initialize git
git initCrear un package.json y tsconfig.json con su administrador de paquetes de elección:
npm init -y
npm exec tsc -- --initpnpm init
pnpm exec tsc --inityarn init -y
yarn exec tsc --initInstala la aplicación y TypeScript dependencias con su administrador de paquetes de elección:
npm i tsoa-next express
npm i -D typescript @types/node @types/expresspnpm add tsoa-next express
pnpm add -D typescript @types/node @types/expressyarn add tsoa-next express
yarn add -D typescript @types/node @types/expressImportaciones de las rutas generadas tsoa-next, por lo que el paquete que instala su aplicación es también el paquete utilizado por los controladores y generado RegisterRoutes archivos. También puede encontrar el paquete publicado en npm.
Configuración tsoa y tipografía
// tsoa.json
{
"entryFile": "src/app.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/**/*Controller.ts"],
"spec": {
"outputDirectory": "build",
"specVersion": 3
},
"routes": {
"routesDir": "build"
}
}Echemos un vistazo a lo que estamos diciendo tsoa Aquí: Primero, especificamos dónde estará el punto de entrada de nuestra aplicación. Lo más probable es que este archivo se llame index.ts o app.ts. Crearemos este archivo en un segundo.
Después, el nivel superior controllerPathGlobs El ajuste dice tsoa donde puede buscar controladores para que no tengamos que importarlos manualmente.
Siguiente, diremos tsoa qué tan estricto exceso de propiedad control (para utilizar el TypeScript término) o adicional Verificación de propiedades (para utilizar OpenAPI La terminología debe ser. Podemos elegir "ignorar" propiedades adicionales (las OpenAPI por defecto), eliminarlos durante la validación ("extras silenciosamente-remove"), o lanzar un Error de vuelta al Cliente ("throw-on-extras"). A continuación, establecemos el directorio de salida para fuera OpenAPI (OEA) y nuestra routes.ts archivo, del que hablaremos más tarde.
Hemos establecido el specVersion a 3 Así que... tsoa generará un OpenAPI v3 especificación. También puede utilizar 3.1 cuando quieras OpenAPI 3.1.
Para una lista completa de todos los posibles config, echa un vistazo a la Referencia de API
TIP
Mientras que el configuración ts predeterminado funcionará para esta guía, un tsconfig mejorado. Json parecería algo así:
Details
{
"compilerOptions": {
/* Basic Options */
"incremental": true,
"target": "es6",
"module": "commonjs",
"outDir": "build",
/* Strict Type-Checking Options */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
/* Additional Checks */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
/* Module Resolution Options */
"moduleResolution": "node",
"baseUrl": ".",
"esModuleInterop": true,
/* Experimental Options */
"experimentalDecorators": true,
// emitDecoratorMetadata is not needed by tsoa-next itself
/* Advanced Options */
"forceConsistentCasingInFileNames": true,
},
}Definir nuestro primer modelo
Si ya tienes un OpenAPI Especificación, puede utilizar ya existente OpenAPI herramienta para generar sus modelos o interfaces. De lo contrario, definamos un User Interfaz en src/users/user.ts.
export interface User {
id: number
email: string
name: string
status?: 'Happy' | 'Sad'
phoneNumbers: string[]
}Antes de comenzar a definir a nuestro Contralor, es generalmente una buena idea crear un Servicio que maneja la interacción con nuestros Modelos en lugar de empuje toda esa lógica en la capa controladora.
// src/users/usersService.ts
import { User } from './user'
// A post request should not contain an id.
export type UserCreationParams = Pick<User, 'email' | 'name' | 'phoneNumbers'>
export class UsersService {
public get(id: number, name?: string): User {
return {
id,
email: 'jane@doe.com',
name: name ?? 'Jane Doe',
status: 'Happy',
phoneNumbers: [],
}
}
public create(userCreationParams: UserCreationParams): User {
return {
id: Math.floor(Math.random() * 10000), // Random
status: 'Happy',
...userCreationParams,
}
}
}Definir un simple controlador
// src/users/usersController.ts
import { Body, Controller, Get, Path, Post, Query, Route, SuccessResponse } from 'tsoa-next'
import { User } from './user'
import { UsersService, UserCreationParams } from './usersService'
@Route('users')
export class UsersController extends Controller {
@Get('{userId}')
public async getUser(@Path() userId: number, @Query() name?: string): Promise<User> {
return new UsersService().get(userId, name)
}
@SuccessResponse('201', 'Created') // Custom success response
@Post()
public async createUser(@Body() requestBody: UserCreationParams): Promise<void> {
this.setStatus(201) // set return status 201
new UsersService().create(requestBody)
return
}
}Vamos a dar un paso atrás y hablar de lo que está pasando aquí. Como usted puede esperar que ya lo digamos, estamos definiendo un /users/ ruta utilizando el @Route() decorador sobre nuestra clase de controlador.
Adicionalmente, definimos 2 métodos: getUser y createUser. El @Get() decorador en combinación con nuestra ruta base /users/ lo dirá tsoa para invocar este método para cada solicitud GET /users/, donde _{user Es una plantilla.
Templatura OpenAPI Path
Rotación en tsoa está muy reflejado OpenAPIEs tentador por razones de compatibilidad. La tentación del camino se refiere al uso de expresiones de plantilla, delimitadas por frenos rizados ({}), para marcar una sección de una ruta URL como reemplazable utilizando parámetros de ruta.
Bajo la capucha, esto sería como definir app.get('users/:userId'). Mientras que express le permite utilizar las definiciones de ruta regex-ish, preferimos dividir el enrutamiento y la validación más claramente. Porque estás pidiendo que el id sea un number usando el @Path() decorador con userId de tipo número, tsoa rechazará pasar aquí un string. De manera similar, si quieres aceptar un string con un determinado patrón, puedes hacerlo usando anotaciones JSON Schema. Usted puede aprender más sobre eso here.
tsoa-next soporta el camino habitual, la consulta, la cabecera y los decoradores del cuerpo, y también admite decoradores multiparte de forma-datos como @FormField(), @UploadedFile(), y @UploadedFiles(), además de parámetros inyectados solo por tiempo de ejecución, como @Request() y @Res().
TIP
Si el nombre del parámetro es igual al parámetro del mensaje http, puede omitir el argumento a los decoradores, de lo contrario puede proporcionar un argumento:
@Query('my-query') myQuery: string;Una lista completa de todos los decoradores se puede encontrar here.
Caveat
Utilice siempre una exportación llamada (export class C) en la clase de controlador para tsoa para recogerlo correctamente. Exportaciones por defecto (export default class C) actualmente no son compatibles.
Creando nuestro servidor expreso
Vamos a crear un app.ts y a server.ts archivo en nuestro directorio fuente como este:
// src/app.ts
import express, { json, urlencoded } from 'express'
import { RegisterRoutes } from '../build/routes'
export const app = express()
// Use body parser to read sent json payloads
app.use(
urlencoded({
extended: true,
}),
)
app.use(json())
RegisterRoutes(app)// src/server.ts
import { app } from './app'
const port = process.env.PORT || 3000
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))Construir los archivos generados
En este punto puede haber notado que TypeScript no encontrará RegisterRoutes de la importación build/routes. Eso es porque no hemos preguntado tsoa para generar el archivo de rutas y OpenAPI espectro todavía. Hagámoslo ahora:
mkdir -p build # Create the build directory if it doesn't existnpm exec tsoa -- spec-and-routespnpm exec tsoa spec-and-routesyarn exec tsoa spec-and-routesAhora sus archivos generados deberían haber sido creados y usted puede compilar TypeScript y comenzar su servidor:
npm exec tsc -- --outDir build --experimentalDecoratorspnpm exec tsc --outDir build --experimentalDecoratorsyarn exec tsc --outDir build --experimentalDecoratorsnode build/src/server.jsTIP
Usted puede querer añadir estos scripts a sus package.json en este punto:
"main": "build/src/server.js",
"scripts": {
"build": "tsoa spec-and-routes && tsc",
"start": "node build/src/server.js"
},¿Qué sigue?
- Invocación manual
tscytsoa routesen el desarrollo no es muy conveniente. - Inspección nuestra primera OpenAPI especificación y superponiendo nuestro bucle de retroalimentación al servir una versión actualizada de SwaggerUI durante el desarrollo.
Podemos mejorarlo usando live reloading.
- Mejorar nuestra respuesta para errores de validación utilizando correctamente error handling- Uso Descriptions, Ejemplos y Annotations para validación avanzada y mejor documentación
