Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Assess and migrate workloads from AWS, GCP, or other clouds to Azure services.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/services/functions/runtimes/javascript.md
1# JavaScript (Node.js) — Azure Functions v4 Triggers & Bindings23> **Model**: JavaScript v4 programming model. **NO** `function.json` files.4> Import: `const { app, input, output } = require('@azure/functions');`56## Lambda Migration Rules78> Shared rules (bindings over SDKs, latest runtime, identity-first auth) → [global-rules.md](../global-rules.md)910JS-specific:11- Use `extraInputs` / `extraOutputs` with binding path expressions (e.g., `{queueTrigger}`) for dynamic blob I/O12- Access metadata via `context.triggerMetadata`13- `package.json`: `"@azure/functions": "^4.0.0"`1415### Correct Migration Pattern1617```javascript18const { app, input, output } = require('@azure/functions');1920// Use bindings for blob I/O instead of BlobServiceClient SDK21const blobInput = input.storageBlob({22path: 'source-container/{queueTrigger}',23connection: 'AzureWebJobsStorage'24});2526const blobOutput = output.storageBlob({27path: 'destination-container/{queueTrigger}',28connection: 'AzureWebJobsStorage'29});3031app.storageQueue('processImage', {32queueName: 'image-processing',33connection: 'AzureWebJobsStorage',34extraInputs: [blobInput],35extraOutputs: [blobOutput],36handler: async (queueItem, context) => {37const sourceBlob = context.extraInputs.get(blobInput);38context.log(`Processing blob: ${queueItem}`);39// Process the blob...40context.extraOutputs.set(blobOutput, processedBuffer);41}42});43```4445> ❌ Do NOT use legacy v1-v3 `module.exports` — always use `app.*()` registration.4647## HTTP Trigger4849```javascript50app.http('httpFunction', {51methods: ['GET', 'POST'],52authLevel: 'anonymous',53handler: async (request, context) => {54const name = request.query.get('name') || (await request.text());55return { body: `Hello, ${name}!` };56}57});58```5960## Blob Storage6162```javascript63// Trigger (use EventGrid source for reliability)64app.storageBlob('blobTrigger', {65path: 'samples-workitems/{name}',66connection: 'AzureWebJobsStorage',67source: 'EventGrid',68handler: async (blob, context) => {69context.log(`Blob: ${context.triggerMetadata.name}, Size: ${blob.length}`);70}71});7273// Input binding74const blobInput = input.storageBlob({75path: 'samples-workitems/{queueTrigger}',76connection: 'AzureWebJobsStorage'77});7879// Output binding80const blobOutput = output.storageBlob({81path: 'samples-output/{name}-out',82connection: 'AzureWebJobsStorage'83});84```8586> **⚠️ Flex Consumption + EventGrid Source Requirements:**87> When using `source: 'EventGrid'` on a Flex Consumption plan, three infrastructure requirements MUST be met or the trigger will silently fail:88>89> 1. **Always-ready instances**: Configure `alwaysReady: [{ name: 'blob', instanceCount: 1 }]` in Bicep. Without this, the trigger group never starts and the Event Grid webhook endpoint is never registered.90> 2. **Queue endpoint**: Set `AzureWebJobsStorage__queueServiceUri` in app settings. The blob extension uses queues internally for poison-message tracking with EventGrid source, even though you're not using a queue trigger.91> 3. **Event Grid subscription via Bicep/ARM**: Do NOT create event subscriptions via CLI — webhook validation times out on Flex Consumption. Deploy as a Bicep resource using `listKeys()` to obtain the `blobs_extension` system key.92>93> See [lambda-to-functions.md](../lambda-to-functions.md#flex-consumption--blob-trigger-with-eventgrid-source) for full Bicep patterns.9495### Using Azure AI Services with UAMI9697When calling Azure AI services (Computer Vision, etc.) from a function, use `DefaultAzureCredential` with explicit UAMI client ID:9899```javascript100const { DefaultAzureCredential } = require('@azure/identity');101const createClient = require('@azure-rest/ai-vision-image-analysis').default;102103const credential = new DefaultAzureCredential({104managedIdentityClientId: process.env.AZURE_CLIENT_ID // Required for UAMI105});106const client = createClient(process.env.COMPUTER_VISION_ENDPOINT, credential);107108const result = await client.path('/imageanalysis:analyze').post({109body: { url: blobUrl },110queryParameters: { features: ['People'] } // Use 'People' for face detection111});112```113114> **Note**: `@azure-rest/ai-vision-image-analysis` is still in beta. Pin explicitly: `"1.0.0-beta.3"` — the `^1.0.0` semver range does NOT resolve.115116## Queue Storage117118```javascript119// Trigger120app.storageQueue('queueTrigger', {121queueName: 'myqueue-items',122connection: 'AzureWebJobsStorage',123handler: async (queueItem, context) => {124context.log('Queue item:', queueItem);125}126});127128// Output129const queueOutput = output.storageQueue({130queueName: 'outqueue',131connection: 'AzureWebJobsStorage'132});133```134135## Timer136137```javascript138app.timer('timerFunction', {139schedule: '0 */5 * * * *', // Every 5 minutes (NCRONTAB)140handler: async (myTimer, context) => {141context.log('Timer fired at:', myTimer.scheduleStatus.last);142}143});144```145146## Event Grid147148```javascript149// Trigger150app.eventGrid('eventGridTrigger', {151handler: async (event, context) => {152context.log('Event:', event.subject, event.eventType);153}154});155156// Output157const eventGridOutput = output.eventGrid({158topicEndpointUri: 'MyEventGridTopicUriSetting',159topicKeySetting: 'MyEventGridTopicKeySetting'160});161```162163## Cosmos DB164165```javascript166// Trigger (Change Feed)167app.cosmosDB('cosmosDBTrigger', {168connectionStringSetting: 'CosmosDBConnection',169databaseName: 'mydb',170containerName: 'mycontainer',171createLeaseContainerIfNotExists: true,172handler: async (documents, context) => {173documents.forEach(doc => context.log('Changed doc:', doc.id));174}175});176177// Input178const cosmosInput = input.cosmosDB({179connectionStringSetting: 'CosmosDBConnection',180databaseName: 'mydb',181containerName: 'mycontainer',182id: '{id}',183partitionKey: '{partitionKey}'184});185186// Output187const cosmosOutput = output.cosmosDB({188connectionStringSetting: 'CosmosDBConnection',189databaseName: 'mydb',190containerName: 'mycontainer'191});192```193194## Service Bus195196```javascript197// Queue Trigger198app.serviceBusQueue('sbQueueTrigger', {199queueName: 'myqueue',200connection: 'ServiceBusConnection',201handler: async (message, context) => {202context.log('Message:', message);203}204});205206// Topic Trigger207app.serviceBusTopic('sbTopicTrigger', {208topicName: 'mytopic',209subscriptionName: 'mysubscription',210connection: 'ServiceBusConnection',211handler: async (message, context) => {212context.log('Topic message:', message);213}214});215216// Output217const sbOutput = output.serviceBusQueue({218queueName: 'outqueue',219connection: 'ServiceBusConnection'220});221```222223## Event Hubs224225```javascript226// Trigger227app.eventHub('eventHubTrigger', {228eventHubName: 'myeventhub',229connection: 'EventHubConnection',230cardinality: 'many',231handler: async (events, context) => {232events.forEach(event => context.log('Event:', event));233}234});235236// Output237const ehOutput = output.eventHub({238eventHubName: 'outeventhub',239connection: 'EventHubConnection'240});241```242243## Table Storage244245```javascript246// Input247const tableInput = input.table({248tableName: 'mytable',249partitionKey: '{partitionKey}',250rowKey: '{rowKey}',251connection: 'AzureWebJobsStorage'252});253254// Output255const tableOutput = output.table({256tableName: 'mytable',257connection: 'AzureWebJobsStorage'258});259```260261## SQL262263```javascript264// Trigger265app.generic('sqlTrigger', {266trigger: { type: 'sqlTrigger', tableName: 'dbo.MyTable', connectionStringSetting: 'SqlConnection' },267handler: async (changes, context) => {268changes.forEach(change => context.log('Change:', change));269}270});271272// Input273const sqlInput = input.sql({274commandText: 'SELECT * FROM dbo.MyTable WHERE Id = @Id',275commandType: 'Text',276parameters: '@Id={id}',277connectionStringSetting: 'SqlConnection'278});279280// Output281const sqlOutput = output.sql({282commandText: 'dbo.MyTable',283connectionStringSetting: 'SqlConnection'284});285```286287## SignalR288289```javascript290// Output291const signalROutput = output.generic({292type: 'signalR',293hubName: 'myhub',294connectionStringSetting: 'AzureSignalRConnectionString'295});296```297298## SendGrid299300```javascript301const sendGridOutput = output.generic({302type: 'sendGrid',303apiKey: 'SendGridApiKey',304from: '[email protected]',305to: '{email}'306});307```308309## Using Bindings with Functions310311```javascript312// Combine trigger with input/output bindings313app.http('processItem', {314methods: ['POST'],315extraInputs: [cosmosInput],316extraOutputs: [queueOutput],317handler: async (request, context) => {318const doc = context.extraInputs.get(cosmosInput);319context.extraOutputs.set(queueOutput, JSON.stringify(doc));320return { body: 'Processed' };321}322});323```324325> Full reference: [Azure Functions JavaScript developer guide](https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node)326