Skip to content

Commit c931922

Browse files
Merge pull request #263344 from ejizba/ej/blogUpdates
Update functions node.js guides based on blog post
2 parents b72ab36 + c30efdd commit c931922

File tree

2 files changed

+197
-35
lines changed

2 files changed

+197
-35
lines changed

articles/azure-functions/functions-node-upgrade-v4.md

Lines changed: 178 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ ms.devlang: javascript
77
# ms.devlang: javascript, typescript
88
ms.custom: devx-track-js
99
ms.topic: how-to
10+
zone_pivot_groups: programming-languages-set-functions-nodejs
1011
---
1112

1213
# Migrate to version 4 of the Node.js programming model for Azure Functions
@@ -25,11 +26,19 @@ Version 4 is designed to provide Node.js developers with the following benefits:
2526

2627
Version 4 of the Node.js programming model requires the following minimum versions:
2728

29+
:::zone pivot="programming-language-javascript"
30+
- [`@azure/functions`](https://www.npmjs.com/package/@azure/functions) npm package v4.0.0
31+
- [Node.js](https://nodejs.org/en/download/releases/) v18+
32+
- [Azure Functions Runtime](./functions-versions.md) v4.25+
33+
- [Azure Functions Core Tools](./functions-run-local.md) v4.0.5382+ (if running locally)
34+
:::zone-end
35+
:::zone pivot="programming-language-typescript"
2836
- [`@azure/functions`](https://www.npmjs.com/package/@azure/functions) npm package v4.0.0
2937
- [Node.js](https://nodejs.org/en/download/releases/) v18+
3038
- [TypeScript](https://www.typescriptlang.org/) v4+
3139
- [Azure Functions Runtime](./functions-versions.md) v4.25+
3240
- [Azure Functions Core Tools](./functions-run-local.md) v4.0.5382+ (if running locally)
41+
:::zone-end
3342

3443
## Include the npm package
3544

@@ -45,14 +54,27 @@ In v4, the [`@azure/functions`](https://www.npmjs.com/package/@azure/functions)
4554
4655
In v4 of the programming model, you can structure your code however you want. The only files that you need at the root of your app are *host.json* and *package.json*.
4756
48-
Otherwise, you define the file structure by setting the `main` field in your *package.json* file. You can set the `main` field to a single file or multiple files by using a [glob pattern](https://wikipedia.org/wiki/Glob_(programming)). Common values for the `main` field might be:
57+
Otherwise, you define the file structure by setting the `main` field in your *package.json* file. You can set the `main` field to a single file or multiple files by using a [glob pattern](https://wikipedia.org/wiki/Glob_(programming)). The following table shows example values for the `main` field:
58+
59+
:::zone pivot="programming-language-javascript"
60+
61+
| Example | Description |
62+
| --- | --- |
63+
| **`src/index.js`** | Register functions from a single root file. |
64+
| **`src/functions/*.js`** | Register each function from its own file. |
65+
| **`src/{index.js,functions/*.js}`** | A combination where you register each function from its own file, but you still have a root file for general app-level code. |
66+
67+
:::zone-end
4968
50-
- TypeScript:
51-
- `dist/src/index.js`
52-
- `dist/src/functions/*.js`
53-
- JavaScript:
54-
- `src/index.js`
55-
- `src/functions/*.js`
69+
:::zone pivot="programming-language-typescript"
70+
71+
| Example | Description |
72+
| --- | --- |
73+
| **`dist/src/index.js`** | Register functions from a single root file. |
74+
| **`dist/src/functions/*.js`** | Register each function from its own file. |
75+
| **`dist/src/{index.js,functions/*.js}`** | A combination where you register each function from its own file, but you still have a root file for general app-level code. |
76+
77+
:::zone-end
5678
5779
> [!TIP]
5880
> Make sure you define a `main` field in your *package.json* file.
@@ -68,39 +90,74 @@ The trigger input, instead of the invocation context, is now the first argument
6890
6991
You no longer have to create and maintain those separate *function.json* configuration files. You can now fully define your functions directly in your TypeScript or JavaScript files. In addition, many properties now have defaults so that you don't have to specify them every time.
7092
93+
:::zone pivot="programming-language-javascript"
94+
7195
# [v4](#tab/v4)
7296
97+
:::code language="javascript" source="~/azure-functions-nodejs-v4/js/src/functions/httpTrigger1.js" :::
98+
99+
# [v3](#tab/v3)
100+
73101
```javascript
74-
const { app } = require("@azure/functions");
102+
module.exports = async function (context, req) {
103+
context.log(`Http function processed request for url "${request.url}"`);
75104
76-
app.http('helloWorld1', {
77-
methods: ['GET', 'POST'],
78-
handler: async (request, context) => {
79-
context.log('Http function processed request');
105+
const name = req.query.name || req.body || 'world';
80106
81-
const name = request.query.get('name')
82-
|| await request.text()
83-
|| 'world';
107+
context.res = {
108+
body: `Hello, ${name}!`
109+
};
110+
};
111+
```
84112
85-
return { body: `Hello, ${name}!` };
86-
}
87-
});
113+
```json
114+
{
115+
"bindings": [
116+
{
117+
"authLevel": "anonymous",
118+
"type": "httpTrigger",
119+
"direction": "in",
120+
"name": "req",
121+
"methods": [
122+
"get",
123+
"post"
124+
]
125+
},
126+
{
127+
"type": "http",
128+
"direction": "out",
129+
"name": "res"
130+
}
131+
]
132+
}
88133
```
89134

135+
---
136+
137+
:::zone-end
138+
139+
:::zone pivot="programming-language-typescript"
140+
141+
# [v4](#tab/v4)
142+
143+
:::code language="typescript" source="~/azure-functions-nodejs-v4/ts/src/functions/httpTrigger1.ts" :::
144+
90145
# [v3](#tab/v3)
91146

92-
```javascript
93-
module.exports = async function (context, req) {
94-
context.log('HTTP function processed a request');
147+
```typescript
148+
import { AzureFunction, Context, HttpRequest } from "@azure/functions"
95149

96-
const name = req.query.name
97-
|| req.body
98-
|| 'world';
150+
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
151+
context.log(`Http function processed request for url "${request.url}"`);
99152

100-
context.res = {
101-
body: `Hello, ${name}!`
102-
};
153+
const name = req.query.name || req.body || 'world';
154+
155+
context.res = {
156+
body: `Hello, ${name}!`
157+
};
103158
};
159+
160+
export default httpTrigger;
104161
```
105162

106163
```json
@@ -121,12 +178,15 @@ module.exports = async function (context, req) {
121178
"direction": "out",
122179
"name": "res"
123180
}
124-
]
181+
],
182+
"scriptFile": "../dist/HttpTrigger1/index.js"
125183
}
126184
```
127185

128186
---
129187

188+
:::zone-end
189+
130190
> [!TIP]
131191
> Move the configuration from your *function.json* file to your code. The type of the trigger corresponds to a method on the `app` object in the new model. For example, if you use an `httpTrigger` type in *function.json*, call `app.http()` in your code to register the function. If you use `timerTrigger`, call `app.timer()`.
132192
@@ -144,22 +204,50 @@ The primary input is also called the *trigger* and is the only required input or
144204

145205
Version 4 supports only one way of getting the trigger input, as the first argument:
146206

207+
:::zone pivot="programming-language-javascript"
208+
147209
```javascript
148-
async function helloWorld1(request, context) {
210+
async function httpTrigger1(request, context) {
149211
const onlyOption = request;
150212
```
151213
214+
:::zone-end
215+
216+
:::zone pivot="programming-language-typescript"
217+
218+
```typescript
219+
async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
220+
const onlyOption = request;
221+
```
222+
223+
:::zone-end
224+
152225
# [v3](#tab/v3)
153226
154227
Version 3 supports several ways of getting the trigger input:
155228
229+
:::zone pivot="programming-language-javascript"
230+
156231
```javascript
157-
async function helloWorld1(context, request) {
232+
async function httpTrigger1(context, request) {
233+
const option1 = request;
234+
const option2 = context.req;
235+
const option3 = context.bindings.req;
236+
```
237+
238+
:::zone-end
239+
240+
:::zone pivot="programming-language-typescript"
241+
242+
```typescript
243+
async function httpTrigger1(context: Context, req: HttpRequest): Promise<void> {
158244
const option1 = request;
159245
const option2 = context.req;
160246
const option3 = context.bindings.req;
161247
```
162248
249+
:::zone-end
250+
163251
---
164252
165253
> [!TIP]
@@ -171,12 +259,28 @@ async function helloWorld1(context, request) {
171259
172260
Version 4 supports only one way of setting the primary output, through the return value:
173261
262+
:::zone pivot="programming-language-javascript"
263+
174264
```javascript
175265
return {
176266
body: `Hello, ${name}!`
177267
};
178268
```
179269
270+
:::zone-end
271+
:::zone pivot="programming-language-typescript"
272+
273+
```typescript
274+
async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
275+
// ...
276+
return {
277+
body: `Hello, ${name}!`
278+
};
279+
}
280+
```
281+
282+
:::zone-end
283+
180284
# [v3](#tab/v3)
181285
182286
Version 3 supports several ways of setting the primary output:
@@ -207,6 +311,28 @@ return {
207311
> [!TIP]
208312
> Make sure you always return the output in your function handler, instead of setting it with the `context` object.
209313
314+
### Context logging
315+
316+
In v4, logging methods were moved to the root `context` object as shown in the following example. For more information about logging, see the [Node.js developer guide](./functions-reference-node.md#logging).
317+
318+
# [v4](#tab/v4)
319+
320+
```javascript
321+
context.log('This is an info log');
322+
context.error('This is an error');
323+
context.warn('This is an error');
324+
```
325+
326+
# [v3](#tab/v3)
327+
328+
```javascript
329+
context.log('This is an info log');
330+
context.log.error('This is an error');
331+
context.log.warn('This is an error');
332+
```
333+
334+
---
335+
210336
### Create a test context
211337
212338
Version 3 doesn't support creating an invocation context outside the Azure Functions runtime, so authoring unit tests can be difficult. Version 4 allows you to create an instance of the invocation context, although the information during tests isn't detailed unless you add it yourself.
@@ -238,7 +364,7 @@ The types use the [`undici`](https://undici.nodejs.org/) package in Node.js. Thi
238364
239365
- *Body*. You can access the body by using a method specific to the type that you want to receive:
240366
241-
```javascript
367+
```javascript
242368
const body = await request.text();
243369
const body = await request.json();
244370
const body = await request.formData();
@@ -301,10 +427,18 @@ The types use the [`undici`](https://undici.nodejs.org/) package in Node.js. Thi
301427
302428
- *Body*:
303429
430+
Use the `body` property to return most types like a `string` or `Buffer`:
431+
304432
```javascript
305433
return { body: "Hello, world!" };
306434
```
307435
436+
Use the `jsonBody` property for the easiest way to return a JSON response:
437+
438+
```javascript
439+
return { jsonBody: { hello: "world" } };
440+
```
441+
308442
- *Header*. You can set the header in two ways, depending on whether you're using the `HttpResponse` class or the `HttpResponseInit` interface:
309443
310444
```javascript
@@ -331,12 +465,12 @@ The types use the [`undici`](https://undici.nodejs.org/) package in Node.js. Thi
331465
return { statusCode: 200 };
332466
```
333467
334-
- *Body*. You can set a body in several ways:
468+
- *Body*. You can set a body in several ways and it's the same regardless of the body type (`string`, `Buffer`, JSON object, etc.):
335469
336470
```javascript
337471
context.res.send("Hello, world!");
338472
context.res.end("Hello, world!");
339-
context.res = { body: "Hello, world!" }
473+
context.res = { body: "Hello, world!" };
340474
return { body: "Hello, world!" };
341475
```
342476
@@ -356,8 +490,18 @@ The types use the [`undici`](https://undici.nodejs.org/) package in Node.js. Thi
356490
357491
---
358492
493+
:::zone pivot="programming-language-javascript"
494+
495+
> [!TIP]
496+
> Update any logic by using the HTTP request or response types to match the new methods.
497+
498+
:::zone-end
499+
:::zone pivot="programming-language-typescript"
500+
359501
> [!TIP]
360-
> Update any logic by using the HTTP request or response types to match the new methods. If you're using TypeScript, you'll get build errors if you use old methods.
502+
> Update any logic by using the HTTP request or response types to match the new methods. You should get TypeScript build errors to help you identify if you're using old methods.
503+
504+
:::zone-end
361505
362506
## Troubleshoot
363507

articles/azure-functions/functions-reference-node.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,25 @@ export default httpTrigger;
270270

271271
::: zone pivot="nodejs-model-v4"
272272

273-
The programming model loads your functions based on the `main` field in your `package.json`. This field can be set to a single file like `src/index.js` or a [glob pattern](https://wikipedia.org/wiki/Glob_(programming)) specifying multiple files like `src/functions/*.js`.
273+
The programming model loads your functions based on the `main` field in your `package.json`. You can set the `main` field to a single file or multiple files by using a [glob pattern](https://wikipedia.org/wiki/Glob_(programming)). The following table shows example values for the `main` field:
274+
275+
# [JavaScript](#tab/javascript)
276+
277+
| Example | Description |
278+
| --- | --- |
279+
| **`src/index.js`** | Register functions from a single root file. |
280+
| **`src/functions/*.js`** | Register each function from its own file. |
281+
| **`src/{index.js,functions/*.js}`** | A combination where you register each function from its own file, but you still have a root file for general app-level code. |
282+
283+
# [TypeScript](#tab/typescript)
284+
285+
| Example | Description |
286+
| --- | --- |
287+
| **`dist/src/index.js`** | Register functions from a single root file. |
288+
| **`dist/src/functions/*.js`** | Register each function from its own file. |
289+
| **`dist/src/{index.js,functions/*.js}`** | A combination where you register each function from its own file, but you still have a root file for general app-level code. |
290+
291+
---
274292

275293
In order to register a function, you must import the `app` object from the `@azure/functions` npm module and call the method specific to your trigger type. The first argument when registering a function is the function name. The second argument is an `options` object specifying configuration for your trigger, your handler, and any other inputs or outputs. In some cases where trigger configuration isn't necessary, you can pass the handler directly as the second argument instead of an `options` object.
276294

0 commit comments

Comments
 (0)