Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este inicio rápido: aprenda a usar TypeSpec para diseñar, generar e implementar una aplicación de API de TypeScript RESTful. TypeSpec es un lenguaje de código abierto para describir las API de servicio en la nube y genera código de cliente y servidor para varias plataformas. Al seguir este inicio rápido, aprenderá a definir el contrato de API una vez y a generar implementaciones coherentes, lo que le ayudará a crear servicios de API más fáciles de mantener y bien documentados.
En esta guía de inicio rápido:
- Definición de la API mediante TypeSpec
- Creación de una aplicación de servidor de API
- Integración de Azure Cosmos DB para el almacenamiento persistente
- Implementación en Azure
- Ejecución y prueba de la API
Important
@typespec/http-server-js el emisor está en este momento en versión preliminar.
Esta información está relacionada con un producto de versión preliminar que puede modificarse sustancialmente antes de su lanzamiento. Microsoft no ofrece ninguna garantía, expresada o implícita, con respecto a la información proporcionada aquí.
Prerequisites
- Una cuenta de Azure activa. Cree una cuenta gratuita si no tiene una.
- Node.js LTS instalado en el sistema.
- TypeScript para escribir y compilar código TypeScript.
- Docker
- Visual Studio Code
- Extensión TypeSpec
- Opcional: Implementación con la CLI para desarrolladores de Azure
Desarrollo con TypeSpec
TypeSpec define la API de forma independiente del lenguaje y genera el servidor de API y la biblioteca cliente para varias plataformas. Esta funcionalidad le permite:
- Define tu contrato de API una vez
- Generación de código de cliente y servidor coherentes
- Centrarse en la implementación de lógica de negocios en lugar de en la infraestructura de API
TypeSpec proporciona administración de servicios de API:
- Lenguaje de definición de API
- Middleware de enrutamiento del lado servidor para API
- Bibliotecas cliente para consumir API
Proporcione solicitudes de cliente e integraciones de servidor:
- Implementación de lógica de negocios en middleware, como servicios de Azure para bases de datos, almacenamiento y mensajería
- Servidor de hospedaje para la API (local o en Azure)
- Scripts de implementación para el aprovisionamiento y despliegue repetibles
Creación de una nueva aplicación TypeSpec
Cree una carpeta para almacenar el servidor de API y los archivos TypeSpec.
mkdir my_typespec_quickstart cd my_typespec_quickstartInstale el compilador TypeSpec globalmente:
npm install -g @typespec/compilerCompruebe TypeSpec instalado correctamente:
tsp --versionInicialice el proyecto TypeSpec:
tsp initResponda a las siguientes preguntas con las respuestas proporcionadas.
- ¿Inicializa un nuevo proyecto aquí? Y
- ¿Selecciona una plantilla de proyecto? API REST genérica
- Escriba un nombre de proyecto: Widgets
- ¿Qué emisores desea usar?
- Documento de OpenAPI 3.1
- Códigos auxiliares del servidor JavaScript
Los emisores de TypeSpec son bibliotecas que utilizan varias API del compilador de TypeSpec para reflejar el proceso de compilación y generar artefactos.
Espere a que se complete la inicialización antes de continuar.
Compile el proyecto:
tsp compile .TypeSpec genera el proyecto predeterminado en
./tsp-output, creando dos carpetas independientes:-
schema es la especificación OpenApi 3. Tenga en cuenta que las pocas líneas en
./main.tspgeneraron más de 200 líneas de la especificación de OpenApi para usted. -
server es el middleware generado. Este middleware se puede incorporar en un proyecto de servidor Node.js.
-
./tsp-output/js/src/generated/models/all/demo-service.tsdefine las interfaces de la API de widgets. -
./tsp-output/js/src/generated/http/openapi3.tsdefine la especificación de Open API como un archivo TypeScript y se vuelve a generar cada vez que compile el proyecto TypeSpec.
-
-
schema es la especificación OpenApi 3. Tenga en cuenta que las pocas líneas en
Configuración de emisores typeSpec
Emplea los archivos TypeSpec para configurar la generación del servidor de API y estructurar completamente el servidor Express.js.
./tsconfig.yamlAbra y reemplace la configuración existente por el código YAML siguiente:emit: - "@typespec/openapi3" - "@typespec/http-server-js" options: "@typespec/openapi3": emitter-output-dir: "{output-dir}/server/schema" openapi-versions: - 3.1.0 "@typespec/http-server-js": emitter-output-dir: "{output-dir}/server" express: trueEsta configuración crea un servidor de API de Express.js completo:
-
express: genere el servidor de API de Express.js, incluida la interfaz de usuario de Swagger. -
emitter-output-dir: genere todo en el directorio./server.
-
Elimine el existente
./tsp-output. No se preocupe, generará el servidor en el paso siguiente.Use el emisor de JavaScript TypeSpec para crear el servidor Express.js:
npx hsjs-scaffoldCambie al nuevo
./tsp-output/serverdirectorio:cd ./tsp-output/serverCompile TypeScript en JavaScript.
tscEjecute el proyecto:
npm startEspere a que la notificación se abra en el explorador.
Abra el explorador y vaya a
http://localhost:3000/.api-docs.
La API TypeSpec predeterminada y el servidor funcionan. Si desea finalizar este servidor de API, agregue la lógica empresarial para admitir las APIs de Widgets en
./tsp-output/server/src/controllers/widgets.ts. La interfaz de usuario está conectada a la API que devuelve datos falsos codificados de forma dura.
Descripción de la estructura de archivos de aplicación
La estructura del proyecto de Express.js que se encuentra en tsp-output/server/ incluye el servidor generado, el package.jsony el middleware para la integración de Azure.
server
├── package.json
├── package-lock.json
├── src
│ ├── controllers
│ │ └── widgets.ts
│ ├── generated
│ │ ├── helpers
│ │ │ ├── datetime.ts
│ │ │ ├── header.ts
│ │ │ ├── http.ts
│ │ │ ├── multipart.ts
│ │ │ ├── router.ts
│ │ │ └── temporal
│ │ │ ├── native.ts
│ │ │ └── polyfill.ts
│ │ ├── http
│ │ │ ├── openapi3.ts
│ │ │ ├── operations
│ │ │ │ └── server-raw.ts
│ │ │ └── router.ts
│ │ └── models
│ │ └── all
│ │ ├── demo-service.ts
│ │ └── typespec.ts
│ ├── index.ts
│ └── swagger-ui.ts
La estructura de archivos del proyecto TypeSpec primario incluye este proyecto de Express.js en tsp-output:
├── tsp-output
├── .gitignore
├── main.tsp
├── package-lock.json
├── package.json
├── tspconfig.yaml
Cambio de la persistencia a Azure Cosmos DB no-sql
Ahora que el servidor básico de API de Express.js funciona, actualice el servidor Express.js para trabajar con Azure Cosmos DB para un almacén de datos persistente. Esto incluye cambios para usar la integración de Cosmos DB en el index.ts middleware. Todos los cambios deben producirse fuera del ./tsp-output/server/src/generated directorio.
En el
./tsp-output/serverdirectorio , agregue Azure Cosmos DB al proyecto:npm install @azure/cosmosAgregue la biblioteca de identidades de Azure para autenticarse en Azure:
npm install @azure/identityCree un
./tsp-output/server/src/azuredirectorio para contener código fuente específico de Azure.Cree el
cosmosClient.tsarchivo en ese directorio para crear un objeto de cliente de Cosmos DB y pegue el código siguiente:import { CosmosClient, Database, Container } from "@azure/cosmos"; import { DefaultAzureCredential } from "@azure/identity"; /** * Interface for CosmosDB configuration settings */ export interface CosmosConfig { endpoint: string; databaseId: string; containerId: string; partitionKey: string; } /** * Singleton class for managing CosmosDB connections */ export class CosmosClientManager { private static instance: CosmosClientManager; private client: CosmosClient | null = null; private config: CosmosConfig | null = null; private constructor() {} /** * Get the singleton instance of CosmosClientManager */ public static getInstance(): CosmosClientManager { if (!CosmosClientManager.instance) { CosmosClientManager.instance = new CosmosClientManager(); } return CosmosClientManager.instance; } /** * Initialize the CosmosDB client with configuration if not already initialized * @param config CosmosDB configuration */ private ensureInitialized(config: CosmosConfig): void { if (!this.client || !this.config) { this.config = config; this.client = new CosmosClient({ endpoint: config.endpoint, aadCredentials: new DefaultAzureCredential(), }); } } /** * Get a database instance, creating it if it doesn't exist * @param config CosmosDB configuration * @returns Database instance */ private async getDatabase(config: CosmosConfig): Promise<Database> { this.ensureInitialized(config); const { database } = await this.client!.databases.createIfNotExists({ id: config.databaseId }); return database; } /** * Get a container instance, creating it if it doesn't exist * @param config CosmosDB configuration * @returns Container instance */ public async getContainer(config: CosmosConfig): Promise<Container> { const database = await this.getDatabase(config); const { container } = await database.containers.createIfNotExists({ id: config.containerId, partitionKey: { paths: [config.partitionKey] } }); return container; } /** * Clean up resources and close connections */ public dispose(): void { this.client = null; this.config = null; } } export const buildError = (error: any, message: string) => { const statusCode = error?.statusCode || 500; return { code: statusCode, message: `${message}: ${error?.message || 'Unknown error'}` }; };Observe que el archivo usa el punto de conexión, la base de datos y el contenedor. No necesita una cadena de conexión ni una clave porque usa la credencial
DefaultAzureCredentialde identidad de Azure. Obtenga más información sobre este método de autenticación segura para entornos locales y de producción .Cree un nuevo controlador de widget,
./tsp-output/server/src/controllers/WidgetsCosmos.tsy pegue el código de integración siguiente para Azure Cosmos DB.import { Widgets, Widget, WidgetList, AnalyzeResult,Error } from "../generated/models/all/demo-service.js"; import { WidgetMergePatchUpdate } from "../generated/models/all/typespec/http.js"; import { CosmosClientManager, CosmosConfig, buildError } from "../azure/cosmosClient.js"; import { HttpContext } from "../generated/helpers/router.js"; import { Container } from "@azure/cosmos"; export interface WidgetDocument extends Widget { _ts?: number; _etag?: string; } /** * Implementation of the Widgets API using Azure Cosmos DB for storage */ export class WidgetsCosmosController implements Widgets<HttpContext> { private readonly cosmosConfig: CosmosConfig; private readonly cosmosManager: CosmosClientManager; private container: Container | null = null; /** * Creates a new instance of WidgetsCosmosController * @param azureCosmosEndpoint Cosmos DB endpoint URL * @param databaseId The Cosmos DB database ID * @param containerId The Cosmos DB container ID * @param partitionKey The partition key path */ constructor(azureCosmosEndpoint: string, databaseId: string, containerId: string, partitionKey: string) { if (!azureCosmosEndpoint) throw new Error("azureCosmosEndpoint is required"); if (!databaseId) throw new Error("databaseId is required"); if (!containerId) throw new Error("containerId is required"); if (!partitionKey) throw new Error("partitionKey is required"); this.cosmosConfig = { endpoint: azureCosmosEndpoint, databaseId: databaseId, containerId: containerId, partitionKey: partitionKey }; this.cosmosManager = CosmosClientManager.getInstance(); } /** * Get the container reference, with caching * @returns The Cosmos container instance */ private async getContainer(): Promise<Container | null> { if (!this.container) { try { this.container = await this.cosmosManager.getContainer(this.cosmosConfig); return this.container; } catch (error: any) { console.error("Container initialization error:", error); throw buildError(error, `Failed to access container ${this.cosmosConfig.containerId}`); } } return this.container; } /** * Create a new widget * @param widget The widget to create * @returns The created widget with assigned ID */ async create(ctx: HttpContext, body: Widget ): Promise<Widget | Error> { const id = body.id; try { const container = await this.getContainer(); if(!container) { return buildError({statusCode:500}, "Container is not initialized"); } if (!body.id) { return buildError({statusCode:400}, "Widget ID is required"); } const response = await container.items.create<Widget>(body, { disableAutomaticIdGeneration: true }); if (!response.resource) { return buildError({statusCode:500}, `Failed to create widget ${body.id}: No resource returned`); } return this.documentToWidget(response.resource); } catch (error: any) { if (error?.statusCode === 409) { return buildError({statusCode:409}, `Widget with id ${id} already exists`); } return buildError(error, `Failed to create widget ${id}`); } } /** * Delete a widget by ID * @param id The ID of the widget to delete */ async delete(ctx: HttpContext, id: string): Promise<void | Error> { try { const container = await this.getContainer(); if(!container) { return buildError({statusCode:500}, "Container is not initialized"); } await container.item(id, id).delete(); } catch (error: any) { if (error?.statusCode === 404) { return buildError({statusCode:404}, `Widget with id ${id} not found`); } return buildError(error, `Failed to delete widget ${id}`); } } /** * Get a widget by ID * @param id The ID of the widget to retrieve * @returns The widget if found */ async read(ctx: HttpContext, id: string): Promise<Widget | Error> { try { const container = await this.getContainer(); if(!container) { return buildError({statusCode:500}, "Container is not initialized"); } const { resource } = await container.item(id, id).read<WidgetDocument>(); if (!resource) { return buildError({statusCode:404}, `Widget with id ${id} not found`); } return this.documentToWidget(resource); } catch (error: any) { return buildError(error, `Failed to read widget ${id}`); } } /** * List all widgets with optional paging * @returns List of widgets */ async list(ctx: HttpContext): Promise<WidgetList | Error> { try { const container = await this.getContainer(); if(!container) { return buildError({statusCode:500}, "Container is not initialized"); } const { resources } = await container.items .query({ query: "SELECT * FROM c" }) .fetchAll(); return { items: resources.map(this.documentToWidget) }; } catch (error: any) { return buildError(error, "Failed to list widgets"); } } /** * Update an existing widget * @param id The ID of the widget to update * @param body The partial widget data to update * @returns The updated widget */ async update( ctx: HttpContext, id: string, body: WidgetMergePatchUpdate, ): Promise<Widget | Error> { try { const container = await this.getContainer(); if(!container) { return buildError({statusCode:500}, "Container is not initialized"); } // First check if the widget exists const { resource: item } = await container.item(id).read<WidgetDocument>(); if (!item) { return buildError({statusCode:404}, `Widget with id ${id} not found`); } // Apply patch updates to the existing widget const updatedWidget: Widget = { ...item, ...body, id }; // Replace the document in Cosmos DB const { resource } = await container.item(id).replace(updatedWidget); if (!resource) { return buildError({statusCode:500}, `Failed to update widget ${id}: No resource returned`); } return this.documentToWidget(resource); } catch (error: any) { return buildError(error, `Failed to update widget ${id}`); } } async analyze(ctx: HttpContext, id: string): Promise<AnalyzeResult | Error> { return { id: "mock-string", analysis: "mock-string", }; } /** * Convert a Cosmos DB document to a Widget */ private documentToWidget(doc: WidgetDocument): Widget { return Object.fromEntries( Object.entries(doc).filter(([key]) => !key.startsWith('_')) ) as Widget; } }Actualice el
./tsp-output/server/src/index.tspara importar el nuevo controlador, obtenga la configuración del entorno de Azure Cosmos DB, y luego, cree el WidgetsCosmosController y páselo al enrutador.// Generated by Microsoft TypeSpec import { WidgetsCosmosController } from "./controllers/WidgetsCosmos.js"; import { createDemoServiceRouter } from "./generated/http/router.js"; import express from "express"; import morgan from "morgan"; import { addSwaggerUi } from "./swagger-ui.js"; const azureCosmosEndpoint = process.env.AZURE_COSMOS_ENDPOINT!; const azureCosmosDatabase = "WidgetDb"; const azureCosmosContainer = "Widgets"; const azureCosmosPartitionKey = "/Id"; const router = createDemoServiceRouter( new WidgetsCosmosController( azureCosmosEndpoint, azureCosmosDatabase, azureCosmosContainer, azureCosmosPartitionKey) ); const PORT = process.env.PORT || 3000; const app = express(); app.use(morgan("dev")); const SWAGGER_UI_PATH = process.env.SWAGGER_UI_PATH || "/.api-docs"; addSwaggerUi(SWAGGER_UI_PATH, app); app.use(router.expressMiddleware); app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); console.log( `API documentation is available at http://localhost:${PORT}${SWAGGER_UI_PATH}`, ); });En un terminal de
./tsp-output/server, compile TypeScript en JavaScript.tscEl proyecto ahora se desarrolla con la integración de Cosmos DB. Vamos a crear los scripts de implementación para crear los recursos de Azure e implementar el proyecto.
Creación de una infraestructura de implementación
Cree los archivos necesarios para tener una implementación repetible con la CLI para desarrolladores de Azure y las plantillas de Bicep.
En la raíz del proyecto TypeSpec, cree un
azure.yamlarchivo de definición de implementación y pegue el siguiente código:# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json name: azure-typespec-scaffold-js metadata: template: azd-init@1.14.0 services: api: project: ./ host: containerapp language: js docker: path: Dockerfile pipeline: provider: github hooks: postprovision: windows: shell: pwsh run: | # Set environment variables for the Container App azd env set AZURE_COSMOS_ENDPOINT "$env:AZURE_COSMOS_ENDPOINT" continueOnError: false interactive: true posix: shell: sh run: | # Set environment variables for the Container App azd env set AZURE_COSMOS_ENDPOINT "$AZURE_COSMOS_ENDPOINT" continueOnError: false interactive: trueObserve que esta configuración hace referencia a todo el proyecto TypeSpec.
En la raíz del proyecto TypeSpec, cree el
./Dockerfileque se usa para compilar el contenedor para Azure Container Apps.# Stage 1: Build stage FROM node:20-alpine AS builder WORKDIR /app # Install TypeScript globally RUN npm install -g typescript # Copy package files first to leverage Docker layer caching COPY package*.json ./ # Create the tsp-output/server directory structure RUN mkdir -p tsp-output/server # Copy server package.json COPY tsp-output/server/package.json ./tsp-output/server/ # Install build and dev dependencies RUN npm i --force --no-package-lock RUN cd tsp-output/server && npm install # Copy the rest of the application code COPY . . # Build the TypeScript code RUN cd tsp-output/server && tsc #--------------------------------------------------------------- # Stage 2: Runtime stage FROM node:20-alpine AS runtime # Set NODE_ENV to production for better performance ENV NODE_ENV=production WORKDIR /app # Copy only the server package files COPY tsp-output/server/package.json ./ # Install only production dependencies RUN npm install # Copy all necessary files from the builder stage # This includes the compiled JavaScript, any static assets, etc. COPY --from=builder /app/tsp-output/server/dist ./dist # Set default port and expose it ENV PORT=3000 EXPOSE 3000 # Run the application CMD ["node", "./dist/src/index.js"]En la raíz del proyecto TypeSpec, cree un
./infradirectorio.Cree un
./infra/main.bicepparamarchivo y copie lo siguiente para definir los parámetros que necesitamos para la implementación:using './main.bicep' param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', 'dev') param location = readEnvironmentVariable('AZURE_LOCATION', 'eastus2') param deploymentUserPrincipalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', '')Esta lista de parámetros proporciona los parámetros mínimos necesarios para esta implementación.
Cree un
./infra/main.biceparchivo y copie lo siguiente para definir los recursos de Azure para el aprovisionamiento y la implementación:metadata description = 'Bicep template for deploying a GitHub App using Azure Container Apps and Azure Container Registry.' targetScope = 'resourceGroup' param serviceName string = 'api' var databaseName = 'WidgetDb' var containerName = 'Widgets' var partitionKey = '/id' @minLength(1) @maxLength(64) @description('Name of the environment that can be used as part of naming resource convention') param environmentName string @minLength(1) @description('Primary location for all resources') param location string @description('Id of the principal to assign database and application roles.') param deploymentUserPrincipalId string = '' var resourceToken = toLower(uniqueString(resourceGroup().id, environmentName, location)) var tags = { 'azd-env-name': environmentName repo: 'https://github.com/typespec' } module managedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { name: 'user-assigned-identity' params: { name: 'identity-${resourceToken}' location: location tags: tags } } module cosmosDb 'br/public:avm/res/document-db/database-account:0.8.1' = { name: 'cosmos-db-account' params: { name: 'cosmos-db-nosql-${resourceToken}' location: location locations: [ { failoverPriority: 0 locationName: location isZoneRedundant: false } ] tags: tags disableKeyBasedMetadataWriteAccess: true disableLocalAuth: true networkRestrictions: { publicNetworkAccess: 'Enabled' ipRules: [] virtualNetworkRules: [] } capabilitiesToAdd: [ 'EnableServerless' ] sqlRoleDefinitions: [ { name: 'nosql-data-plane-contributor' dataAction: [ 'Microsoft.DocumentDB/databaseAccounts/readMetadata' 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' ] } ] sqlRoleAssignmentsPrincipalIds: union( [ managedIdentity.outputs.principalId ], !empty(deploymentUserPrincipalId) ? [deploymentUserPrincipalId] : [] ) sqlDatabases: [ { name: databaseName containers: [ { name: containerName paths: [ partitionKey ] } ] } ] } } module containerRegistry 'br/public:avm/res/container-registry/registry:0.5.1' = { name: 'container-registry' params: { name: 'containerreg${resourceToken}' location: location tags: tags acrAdminUserEnabled: false anonymousPullEnabled: true publicNetworkAccess: 'Enabled' acrSku: 'Standard' } } var containerRegistryRole = subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec' ) module registryUserAssignment 'br/public:avm/ptn/authorization/resource-role-assignment:0.1.1' = if (!empty(deploymentUserPrincipalId)) { name: 'container-registry-role-assignment-push-user' params: { principalId: deploymentUserPrincipalId resourceId: containerRegistry.outputs.resourceId roleDefinitionId: containerRegistryRole } } module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.7.0' = { name: 'log-analytics-workspace' params: { name: 'log-analytics-${resourceToken}' location: location tags: tags } } module containerAppsEnvironment 'br/public:avm/res/app/managed-environment:0.8.0' = { name: 'container-apps-env' params: { name: 'container-env-${resourceToken}' location: location tags: tags logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId zoneRedundant: false } } module containerAppsApp 'br/public:avm/res/app/container-app:0.9.0' = { name: 'container-apps-app' params: { name: 'container-app-${resourceToken}' environmentResourceId: containerAppsEnvironment.outputs.resourceId location: location tags: union(tags, { 'azd-service-name': serviceName }) ingressTargetPort: 3000 ingressExternal: true ingressTransport: 'auto' stickySessionsAffinity: 'sticky' scaleMaxReplicas: 1 scaleMinReplicas: 1 corsPolicy: { allowCredentials: true allowedOrigins: [ '*' ] } managedIdentities: { systemAssigned: false userAssignedResourceIds: [ managedIdentity.outputs.resourceId ] } secrets: { secureList: [ { name: 'azure-cosmos-db-nosql-endpoint' value: cosmosDb.outputs.endpoint } { name: 'user-assigned-managed-identity-client-id' value: managedIdentity.outputs.clientId } ] } containers: [ { image: 'mcr.microsoft.com/devcontainers/typescript-node' name: serviceName resources: { cpu: '0.25' memory: '.5Gi' } env: [ { name: 'AZURE_COSMOS_ENDPOINT' secretRef: 'azure-cosmos-db-nosql-endpoint' } { name: 'AZURE_CLIENT_ID' secretRef: 'user-assigned-managed-identity-client-id' } ] } ] } } output AZURE_COSMOS_ENDPOINT string = cosmosDb.outputs.endpoint output AZURE_COSMOS_DATABASE string = databaseName output AZURE_COSMOS_CONTAINER string = containerName output AZURE_COSMOS_PARTITION_KEY string = partitionKey output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer output AZURE_CONTAINER_REGISTRY_NAME string = containerRegistry.outputs.nameLas variables OUTPUT permiten usar los recursos en la nube aprovisionados con el desarrollo local.
Implementación de una aplicación en Azure
Puede implementar esta aplicación en Azure mediante Azure Container Apps:
En un terminal en la raíz del proyecto, autentíquese en la CLI para desarrolladores de Azure:
azd auth loginImplemente en Azure Container Apps mediante la CLI para desarrolladores de Azure:
azd upResponda a las siguientes preguntas con las respuestas proporcionadas.
- Escriba un nombre de entorno único:
tsp-server-js - Selección de una suscripción de Azure que se va a usar: seleccione la suscripción.
- Selección de una ubicación de Azure que se va a usar: seleccione una ubicación cercana.
- Selección de un grupo de recursos que se va a usar: seleccione Crear un nuevo grupo de recursos.
- Escriba un nombre para el nuevo grupo de recursos: acepte el valor predeterminado proporcionado.
- Escriba un nombre de entorno único:
Espere hasta que se complete la implementación. La respuesta incluye información similar a la siguiente:
Deploying services (azd deploy) (✓) Done: Deploying service api - Endpoint: https://container-app-123.ambitiouscliff-456.centralus.azurecontainerapps.io/ SUCCESS: Your up workflow to provision and deploy to Azure completed in 6 minutes 32 seconds.
Uso de la aplicación en el explorador
Una vez implementado, puede hacer lo siguiente:
- En la consola, seleccione la
Endpointdirección URL para abrirla en un explorador. - Agregue la ruta,
/.api-docs, al punto de conexión para usar la interfaz de usuario de Swagger. - Use la característica Pruébelo ahora en cada método para crear, leer, actualizar y eliminar widgets a través de la API.
Desarrollar tu aplicación
Ahora que ha funcionado todo el proceso de un extremo a otro, siga compilando la API:
- Obtenga más información sobre el lenguaje TypeSpec para agregar más API y características de capa de API en
./main.tsp. - Agregue más emisores y configure sus parámetros en .
./tspconfig.yaml - A medida que agregue más características en los archivos TypeSpec, admita esos cambios con código fuente en el proyecto de servidor.
- Siga usando la autenticación sin contraseña con Azure Identity.
Limpieza de recursos
Cuando haya terminado con este inicio rápido, puede quitar los recursos de Azure:
azd down
O elimine el grupo de recursos directamente desde Azure Portal.