Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dalam panduan cepat ini: pelajari cara menggunakan TypeSpec untuk merancang, menghasilkan, dan menerapkan aplikasi API TypeScript RESTful. TypeSpec adalah bahasa sumber terbuka untuk menjelaskan API layanan cloud dan menghasilkan kode klien dan server untuk beberapa platform. Dengan mengikuti mulai cepat ini, Anda mempelajari cara menentukan kontrak API sekali dan menghasilkan implementasi yang konsisten, membantu Anda membangun layanan API yang lebih terawat dan terdokumen dengan baik.
Dalam panduan singkat ini, Anda akan:
- Tentukan API Anda menggunakan TypeSpec
- Membuat aplikasi server API
- Mengintegrasikan Azure Cosmos DB untuk penyimpanan persisten
- Sebarkan ke Azure
- Menjalankan dan menguji API Anda
Important
@typespec/http-server-js pemancar saat ini dalam PRATINJAU.
Informasi ini berkaitan dengan produk prarilis yang mungkin dimodifikasi secara substansial sebelum dirilis. Microsoft tidak memberikan jaminan, tersurat maupun tersirat, sehubungan dengan informasi yang diberikan di sini.
Prerequisites
- Akun Azure aktif. Buat akun secara gratis jika Anda tidak memilikinya.
- Node.js LTS terinstal pada sistem Anda.
- TypeScript untuk menulis dan mengkompilasi kode TypeScript.
- Docker
- Visual Studio Code
- Ekstensi TypeSpec
- Opsional: Penyebaran dengan Azure Developer CLI
Mengembangkan dengan TypeSpec
TypeSpec mendefinisikan API Anda dengan cara bahasa agnostik dan menghasilkan server API dan pustaka klien untuk beberapa platform. Fungsionalitas ini memungkinkan Anda untuk:
- Tentukan kontrak API Anda sekali
- Hasilkan server dan kode klien yang konsisten
- Fokus pada penerapan logika bisnis daripada infrastruktur API
TypeSpec menyediakan manajemen layanan API:
- Bahasa definisi API
- Middleware untuk perutean sisi server bagi API
- Pustaka bagi klien untuk memanfaatkan API
Anda menyediakan permintaan klien dan integrasi server:
- Menerapkan logika bisnis di middleware seperti layanan Azure untuk database, penyimpanan, dan olahpesan
- Hosting server untuk API Anda (secara lokal atau di Azure)
- Skrip penyebaran untuk penyediaan dan penyebaran berulang
Membuat aplikasi TypeSpec baru
Buat folder baru untuk menyimpan server API dan file TypeSpec.
mkdir my_typespec_quickstart cd my_typespec_quickstartInstal pengkompilasi TypeSpec secara global:
npm install -g @typespec/compilerPeriksa TypeSpec yang terinstal dengan benar:
tsp --versionInisialisasi proyek TypeSpec:
tsp initJawab perintah berikut dengan jawaban yang disediakan:
- Menginisialisasi proyek baru di sini? Y
- Pilih templat proyek? API REST umum
- Masukkan nama proyek: Widget
- Emiter apa yang ingin Anda gunakan?
- Dokumen OpenAPI 3.1
- Stub JavaScript server
TypeSpec emitters adalah pustaka yang menggunakan berbagai API kompilator TypeSpec untuk merefleksikan proses kompilasi TypeSpec dan menghasilkan artefak.
Tunggu hingga inisialisasi selesai sebelum melanjutkan.
Kompilasi proyek:
tsp compile .TypeSpec menghasilkan proyek default di
./tsp-output, membuat dua folder terpisah:-
skema adalah spesifikasi OpenApi 3. Perhatikan bahwa beberapa baris dalam
./main.tspdihasilkan lebih dari 200 baris spesifikasi OpenApi untuk Anda. -
server adalah middleware yang dihasilkan. Middleware ini dapat dimasukkan ke dalam proyek server Node.js.
-
./tsp-output/js/src/generated/models/all/demo-service.tsmenentukan antarmuka untuk WIDGET API. -
./tsp-output/js/src/generated/http/openapi3.tsmenentukan spesifikasi Open API sebagai file TypeScript dan diregenerasi setiap kali Anda mengkompilasi proyek TypeSpec Anda.
-
-
skema adalah spesifikasi OpenApi 3. Perhatikan bahwa beberapa baris dalam
Mengonfigurasi pengemisi TypeSpec
Gunakan file TypeSpec untuk mengonfigurasi pembuatan server API untuk membuat perancah seluruh server Express.js.
./tsconfig.yamlBuka dan ganti konfigurasi yang ada dengan YAML berikut: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: trueKonfigurasi ini membuat server API Express.js lengkap:
-
express: Hasilkan server API Express.js, termasuk antarmuka pengguna Swagger. -
emitter-output-dir: Hasilkan semuanya ke direktori./server.
-
Hapus
./tsp-outputyang ada. Jangan khawatir, Anda akan menghasilkan server di langkah berikutnya.Gunakan pemancar TypeSpec JavaScript untuk membuat server Express.js:
npx hsjs-scaffoldUbah ke direktori baru
./tsp-output/server:cd ./tsp-output/serverKompilasikan TypeScript ke dalam JavaScript.
tscJalankan proyek:
npm startTunggu pemberitahuan untuk Membuka di browser.
Buka browser dan buka
http://localhost:3000/.api-docs.
TypeSpec API dan server default berfungsi. Jika Anda ingin menyelesaikan server API ini, tambahkan logika bisnis Anda untuk mendukung API Widget di
./tsp-output/server/src/controllers/widgets.ts. UI terhubung ke API yang mengembalikan data palsu yang dikodekan secara permanen.
Memahami struktur file aplikasi
Struktur proyek Express.js yang ditemukan di tsp-output/server/ mencakup server yang dihasilkan, package.json, dan middleware untuk integrasi Azure Anda.
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
Struktur file untuk proyek TypeSpec induk mencakup proyek Express.js ini di tsp-output:
├── tsp-output
├── .gitignore
├── main.tsp
├── package-lock.json
├── package.json
├── tspconfig.yaml
Mengubah persistensi ke Azure Cosmos DB no-sql
Sekarang setelah server API Express.js dasar berfungsi, perbarui server Express.js untuk bekerja dengan Azure Cosmos DB untuk penyimpanan data persisten. Ini termasuk perubahan pada index.ts untuk menggunakan integrasi Cosmos DB di middleware. Semua perubahan harus terjadi di luar direktori ./tsp-output/server/src/generated.
./tsp-output/serverDi direktori, tambahkan Azure Cosmos DB ke proyek:npm install @azure/cosmosTambahkan pustaka Azure Identity untuk mengautentikasi ke Azure:
npm install @azure/identityBuat
./tsp-output/server/src/azuredirektori untuk menyimpan kode sumber khusus untuk Azure.Buat file di direktori tersebut
cosmosClient.tsuntuk membuat objek klien Cosmos DB dan tempelkan dalam kode berikut: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'}` }; };Perhatikan bahwa file menggunakan titik akhir, database, dan kontainer. Ini tidak memerlukan string koneksi atau kunci karena menggunakan kredensial
DefaultAzureCredentialAzure Identity . Pelajari selengkapnya tentang metode autentikasi aman ini untuk lingkungan lokal dan produksi .Buat pengontrol Widget baru,
./tsp-output/server/src/controllers/WidgetsCosmos.ts, dan tempelkan kode integrasi berikut untuk 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; } }Perbarui
./tsp-output/server/src/index.tsuntuk mengimpor pengontrol baru, dapatkan pengaturan lingkungan Azure Cosmos DB, lalu buat WidgetsCosmosController dan sampaikan ke router.// 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}`, ); });Di terminal
./tsp-output/server, kompilasi TypeScript menjadi JavaScript.tscProyek ini sekarang dibangun dengan integrasi Cosmos DB. Mari kita buat skrip penyebaran untuk membuat sumber daya Azure dan menyebarkan proyek.
Membuat infrastruktur penyebaran
Buat file yang diperlukan untuk melakukan penyebaran berulang dengan Azure Developer CLI dan templat Bicep.
Di akar proyek TypeSpec, buat
azure.yamlfile definisi penyebaran dan tempelkan di sumber berikut:# 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: truePerhatikan bahwa konfigurasi ini mereferensikan seluruh proyek TypeSpec.
Di akar proyek TypeSpec, buat
./Dockerfileyang digunakan untuk membangun kontainer untuk 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"]Di akar proyek TypeSpec, buat
./infradirektori.Buat file
./infra/main.bicepparamdan salin konten berikut untuk menentukan parameter yang kita butuhkan untuk deployment:using './main.bicep' param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', 'dev') param location = readEnvironmentVariable('AZURE_LOCATION', 'eastus2') param deploymentUserPrincipalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', '')Daftar param ini menyediakan parameter minimum yang diperlukan untuk penyebaran ini.
Buat file
./infra/main.bicepdan salin yang berikut ini untuk menentukan sumber daya Azure untuk provisi dan penyebaran.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.nameVariabel OUTPUT memungkinkan Anda menggunakan sumber daya cloud yang disediakan dengan pengembangan lokal Anda.
Menyebarkan aplikasi ke Azure
Anda dapat menyebarkan aplikasi ini ke Azure menggunakan Azure Container Apps:
Di terminal di akar proyek, autentikasi ke Azure Developer CLI:
azd auth loginSebarkan ke Azure Container Apps menggunakan Azure Developer CLI:
azd upJawab perintah berikut dengan jawaban yang disediakan:
- Masukkan nama lingkungan yang unik:
tsp-server-js - Pilih Langganan Azure yang akan digunakan: pilih langganan Anda
- Pilih lokasi Azure yang akan digunakan: pilih lokasi di dekat Anda
- Pilih grup sumber daya yang akan digunakan: Pilih Buat grup sumber daya baru
- Masukkan nama untuk grup sumber daya baru: terima default yang disediakan
- Masukkan nama lingkungan yang unik:
Tunggu hingga proses penyebaran selesai. Respons mencakup informasi yang mirip dengan yang berikut ini:
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.
Menggunakan aplikasi di browser
Setelah disebarkan, Anda dapat:
- Di konsol, pilih
Endpointurl untuk membukanya di browser. - Tambahkan rute,
/.api-docs, ke titik akhir untuk menggunakan antarmuka pengguna Swagger. - Gunakan fitur Coba sekarang pada setiap metode untuk membuat, membaca, memperbarui, dan menghapus widget melalui API.
Kembangkan aplikasi Anda
Sekarang setelah Anda memiliki seluruh proses end-to-end yang berfungsi, terus bangun API Anda:
- Pelajari selengkapnya tentang bahasa TypeSpec untuk menambahkan lebih banyak API dan fitur lapisan API di
./main.tsp. - Tambahkan lebih banyak emiter dan konfigurasikan parameternya di
./tspconfig.yaml. - Saat Anda menambahkan lebih banyak fitur dalam file TypeSpec Anda, dukung perubahan tersebut dengan kode sumber di proyek server.
- Terus gunakan autentikasi tanpa kata sandi dengan Azure Identity.
Membersihkan sumber daya
Setelah selesai dengan panduan cepat ini, Anda dapat menghapus sumber daya Azure.
azd down
Atau hapus grup sumber daya langsung dari portal Microsoft Azure.