Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
GitHub Copilot for Azure plugin providing Azure service management and development assistance inside Claude Code and IDEs.
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