Skip to content

Commit c2589a4

Browse files
committed
Merge branch 'main' of https://github.com/MicrosoftDocs/azure-docs-pr into heidist-refresh
2 parents 0ec57ff + e0b863d commit c2589a4

23 files changed

+982
-470
lines changed

.openpublishing.redirection.azure-monitor.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
"redirect_url": "/azure/azure-monitor/app/app-insights-overview",
4646
"redirect_document_id": false
4747
},
48+
{
49+
"source_path_from_root": "/articles/azure-monitor/app/console.md",
50+
"redirect_url": "/previous-versions/azure/azure-monitor/app/console",
51+
"redirect_document_id": false
52+
},
4853
{
4954
"source_path_from_root": "/articles/azure-monitor/app/resource-manager-web-app.md",
5055
"redirect_url": "/previous-versions/azure/azure-monitor/app/resource-manager-web-app",

articles/azure-functions/TOC.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,16 +375,20 @@
375375
href: dotnet-isolated-in-process-differences.md
376376
- name: F#
377377
href: functions-reference-fsharp.md
378-
- name: JavaScript
379-
href: functions-reference-node.md
378+
- name: Node.js
379+
items:
380+
- name: JavaScript
381+
href: functions-reference-node.md
382+
- name: TypeScript
383+
href: functions-reference-node.md#typescript
384+
- name: Upgrade to model v4.x
385+
href: functions-node-upgrade-v4.md
380386
- name: Java
381387
href: functions-reference-java.md
382388
- name: PowerShell
383389
href: functions-reference-powershell.md
384390
- name: Python
385391
href: functions-reference-python.md
386-
- name: TypeScript
387-
href: functions-reference-node.md#typescript
388392
- name: How-to guides
389393
items:
390394
- name: Develop
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
---
2+
title: Upgrade to v4 of the Node.js model for Azure Functions
3+
description: This article shows you how to upgrade your existing function apps running on v3 of the Node.js programming model to v4.
4+
ms.service: azure-functions
5+
ms.date: 03/15/2023
6+
ms.devlang: javascript, typescript
7+
ms.topic: how-to
8+
---
9+
10+
# Upgrade to version 4 of the Node.js programming model for Azure Functions
11+
12+
This article discusses the differences between version 3 and version 4 of the Node.js programming model and how to upgrade an existing v3 app. If you want to create a brand new v4 app instead of upgrading an existing v3 app, see the tutorial for either [VS Code](./create-first-function-cli-node.md) or [Azure Functions Core Tools](./create-first-function-vs-code-node.md). This article uses "TIP" sections to highlight the most important concrete actions you should take to upgrade your app.
13+
14+
Version 4 was designed with the following goals in mind:
15+
16+
- Provide a familiar and intuitive experience to Node.js developers
17+
- Make the file structure flexible with support for full customization
18+
- Switch to a code-centric approach for defining function configuration
19+
20+
[!INCLUDE [Programming Model Considerations](../../includes/functions-nodejs-model-considerations.md)]
21+
22+
## Requirements
23+
24+
Version 4 of the Node.js programming model requires the following minimum versions:
25+
26+
- [`@azure/functions`](https://www.npmjs.com/package/@azure/functions) npm package v4.0.0-alpha.8+
27+
- [Node.js](https://nodejs.org/en/download/releases/) v18+
28+
- [TypeScript](https://www.typescriptlang.org/) v4+
29+
- [Azure Functions Runtime](./functions-versions.md) v4.16+
30+
- [Azure Functions Core Tools](./functions-run-local.md) v4.0.4915+ (if running locally)
31+
32+
## Include the npm package
33+
34+
For the first time, the [`@azure/functions`](https://www.npmjs.com/package/@azure/functions) npm package contains the primary source code that backs the Node.js programming model. In previous versions, that code shipped directly in Azure and the npm package only had the TypeScript types. Moving forward, you need to include this package for both TypeScript and JavaScript apps. You _can_ include the package for existing v3 apps, but it isn't required.
35+
36+
> [!TIP]
37+
> Make sure the `@azure/functions` package is listed in the `dependencies` section (not `devDependencies`) of your `package.json` file. You can install v4 with the command
38+
> ```
39+
> npm install @azure/functions@preview
40+
> ```
41+
42+
## Set your app entry point
43+
44+
In v4 of the programming model, you can structure your code however you want. The only files you need at the root of your app are `host.json` and `package.json`. Otherwise, you define the file structure by setting the `main` field in your `package.json` file. The `main` field can be set to a single file or multiple files by using a [glob pattern](https://wikipedia.org/wiki/Glob_(programming)). Common values for the `main` field may be:
45+
- TypeScript
46+
- `dist/src/index.js`
47+
- `dist/src/functions/*.js`
48+
- JavaScript
49+
- `src/index.js`
50+
- `src/functions/*.js`
51+
52+
> [!TIP]
53+
> Make sure you define a `main` field in your `package.json` file
54+
55+
## Switch the order of arguments
56+
57+
The trigger input is now the first argument to your function handler instead of the invocation context. The invocation context, now the second argument, was simplified in v4 and isn't as required as the trigger input - it can be left off if you aren't using it.
58+
59+
> [!TIP]
60+
> Switch the order of your arguments. For example if you are using an http trigger, switch `(context, request)` to either `(request, context)` or just `(request)` if you aren't using the context.
61+
62+
## Define your function in code
63+
64+
Say goodbye 👋 to `function.json` files! All of the configuration that was previously specified in a `function.json` file is now defined directly in your TypeScript or JavaScript files. In addition, many properties now have a default so that you don't have to specify them every time.
65+
66+
# [v4](#tab/v4)
67+
68+
```javascript
69+
const { app } = require("@azure/functions");
70+
71+
app.http('helloWorld1', {
72+
methods: ['GET', 'POST'],
73+
handler: async (request, context) => {
74+
context.log('Http function processed request');
75+
76+
const name = request.query.get('name')
77+
|| await request.text()
78+
|| 'world';
79+
80+
return { body: `Hello, ${name}!` };
81+
}
82+
});
83+
```
84+
85+
# [v3](#tab/v3)
86+
87+
```javascript
88+
module.exports = async function (context, req) {
89+
context.log('HTTP function processed a request');
90+
91+
const name = req.query.name
92+
|| req.body
93+
|| 'world';
94+
95+
context.res = {
96+
body: `Hello, ${name}!`
97+
};
98+
};
99+
```
100+
101+
```json
102+
{
103+
"bindings": [
104+
{
105+
"authLevel": "anonymous",
106+
"type": "httpTrigger",
107+
"direction": "in",
108+
"name": "req",
109+
"methods": [
110+
"get",
111+
"post"
112+
]
113+
},
114+
{
115+
"type": "http",
116+
"direction": "out",
117+
"name": "res"
118+
}
119+
]
120+
}
121+
```
122+
123+
---
124+
125+
> [!TIP]
126+
> Move the config from your `function.json` file to your code. The type of the trigger will correspond to a method on the `app` object in the new model. For example, if you use an `httpTrigger` type in `function.json`, you will now call `app.http()` in your code to register the function. If you use `timerTrigger`, you will now call `app.timer()` and so on.
127+
128+
129+
## Review your usage of context
130+
131+
The `context` object has been simplified to reduce duplication and make it easier to write unit tests. For example, we streamlined the primary input and output so that they're only accessed as the argument and return value of your function handler. The primary input and output can't be accessed on the `context` object anymore, but you must still access _secondary_ inputs and outputs on the `context` object. For more information about secondary inputs and outputs, see the [Node.js developer guide](./functions-reference-node.md#extra-inputs-and-outputs).
132+
133+
### Get the primary input as an argument
134+
135+
The primary input is also called the "trigger" and is the only required input or output. You must have one and only one trigger.
136+
137+
# [v4](#tab/v4)
138+
139+
v4 only supports one way of getting the trigger input, as the first argument.
140+
141+
```javascript
142+
async function helloWorld1(request, context) {
143+
const onlyOption = request;
144+
```
145+
146+
# [v3](#tab/v3)
147+
148+
v3 supports several different ways of getting the trigger input.
149+
150+
```javascript
151+
async function helloWorld1(context, request) {
152+
const option1 = request;
153+
const option2 = context.req;
154+
const option3 = context.bindings.req;
155+
```
156+
157+
---
158+
159+
> [!TIP]
160+
> Make sure you aren't using `context.req` or `context.bindings` to get the input.
161+
162+
### Set the primary output as your return value
163+
164+
# [v4](#tab/v4)
165+
166+
v4 only supports one way of setting the primary output, through the return value.
167+
168+
```javascript
169+
return {
170+
body: `Hello, ${name}!`
171+
};
172+
```
173+
174+
# [v3](#tab/v3)
175+
176+
v3 supports several different ways of setting the primary output.
177+
178+
```javascript
179+
// Option 1
180+
context.res = {
181+
body: `Hello, ${name}!`
182+
};
183+
// Option 2, but you can't use this option with any async code:
184+
context.done(null, {
185+
body: `Hello, ${name}!`
186+
});
187+
// Option 3, but you can't use this option with any async code:
188+
context.res.send(`Hello, ${name}!`);
189+
// Option 4, if "name" in "function.json" is "res":
190+
context.bindings.res = {
191+
body: `Hello, ${name}!`
192+
}
193+
// Option 5, if "name" in "function.json" is "$return":
194+
return {
195+
body: `Hello, ${name}!`
196+
};
197+
```
198+
199+
---
200+
201+
> [!TIP]
202+
> Make sure you are always returning the output in your function handler, instead of setting it with the `context` object.
203+
204+
### Create a test context
205+
206+
v3 doesn't support creating an invocation context outside of the Azure Functions runtime, making it difficult to author unit tests. v4 allows you to create an instance of the invocation context, although the information during tests isn't detailed unless you add it yourself.
207+
208+
# [v4](#tab/v4)
209+
210+
```javascript
211+
const testInvocationContext = new InvocationContext({
212+
functionName: 'testFunctionName',
213+
invocationId: 'testInvocationId'
214+
});
215+
```
216+
217+
# [v3](#tab/v3)
218+
219+
Not possible 😮
220+
221+
---
222+
223+
## Review your usage of HTTP types
224+
225+
The http request and response types are now a subset of the [fetch standard](https://developer.mozilla.org/docs/Web/API/fetch) instead of being types unique to Azure Functions. The types use Node.js's [`undici`](https://undici.nodejs.org/) package, which follows the fetch standard and is [currently being integrated](https://github.com/nodejs/undici/issues/1737) into Node.js core.
226+
227+
### HttpRequest
228+
229+
# [v4](#tab/v4)
230+
- _**Body**_. You can access the body using a method specific to the type you would like to receive:
231+
```javascript
232+
const body = await request.text();
233+
const body = await request.json();
234+
const body = await request.formData();
235+
const body = await request.arrayBuffer();
236+
const body = await request.blob();
237+
```
238+
- _**Header**_:
239+
```javascript
240+
const header = request.headers.get('content-type');
241+
```
242+
- _**Query param**_:
243+
```javascript
244+
const name = request.query.get('name');
245+
```
246+
247+
# [v3](#tab/v3)
248+
- _**Body**_. You can access the body in several ways, but the type returned isn't always consistent:
249+
```javascript
250+
// returns a string, object, or Buffer
251+
const body = request.body;
252+
// returns a string
253+
const body = request.rawBody;
254+
// returns a Buffer
255+
const body = request.bufferBody;
256+
// returns an object representing a form
257+
const body = await request.parseFormBody();
258+
```
259+
- _**Header**_. A header can be retrieved in several different ways:
260+
```javascript
261+
const header = request.get('content-type');
262+
const header = request.headers.get('content-type');
263+
const header = context.bindingData.headers['content-type'];
264+
```
265+
- _**Query param**_:
266+
```javascript
267+
const name = request.query.name;
268+
```
269+
---
270+
271+
### HttpResponse
272+
273+
# [v4](#tab/v4)
274+
- _**Status**_:
275+
```javascript
276+
return { status: 200 };
277+
```
278+
- _**Body**_:
279+
```javascript
280+
return { body: "Hello, world!" };
281+
```
282+
- _**Header**_. You can set the header in two ways, depending if you're using the `HttpResponse` class or `HttpResponseInit` interface:
283+
```javascript
284+
const response = new HttpResponse();
285+
response.headers.set('content-type', 'application/json');
286+
return response;
287+
```
288+
```javascript
289+
return {
290+
headers: { 'content-type': 'application/json' }
291+
};
292+
```
293+
294+
# [v3](#tab/v3)
295+
- _**Status**_. A status can be set in several different ways:
296+
```javascript
297+
context.res.status(200);
298+
context.res = { status: 200}
299+
context.res = { statusCode: 200 };
300+
return { status: 200};
301+
return { statusCode: 200 };
302+
```
303+
- _**Body**_. A body can be set in several different ways:
304+
```javascript
305+
context.res.send("Hello, world!");
306+
context.res.end("Hello, world!");
307+
context.res = { body: "Hello, world!" }
308+
return { body: "Hello, world!" };
309+
```
310+
- _**Header**_. A header can be set in several different ways:
311+
```javascript
312+
response.set('content-type', 'application/json');
313+
response.setHeader('content-type', 'application/json');
314+
response.headers = { 'content-type': 'application/json' }
315+
context.res = {
316+
headers: { 'content-type': 'application/json' }
317+
};
318+
return {
319+
headers: { 'content-type': 'application/json' }
320+
};
321+
```
322+
323+
---
324+
325+
> [!TIP]
326+
> Update any logic using the http request or response types to match the new methods. If you are using TypeScript, you should receive build errors if you use old methods.
327+
328+
## Troubleshooting
329+
330+
If you see the following error, make sure you [set the `EnableWorkerIndexing` flag](./functions-reference-node.md#enable-v4-programming-model) and you're using the minimum version of all [requirements](#requirements):
331+
332+
> No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

0 commit comments

Comments
 (0)