DEV Community

Cover image for Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application
Glaucia Lemos for Microsoft Azure

Posted on • Originally published at techcommunity.microsoft.com

Step by Step Guide: Migrating v3 to v4 programming model for Azure Functions for Node.Js Application

Welcome!

Hi, Developers! Today I would like to teach you how we can migrate to version 4 of the Node.js programming model for Azure Functions, using a real case project: Contoso Real Estate.

Join us for Hack Together: JavaScript on Azure Global Hack from Aug 16 - 31, 2023

Get ready to dive into a world of innovation and growth as we embark on an exciting journey together. Today, I'm thrilled to guide you through an essential skill that will not only enhance your development prowess but also open doors to limitless possibilities in the tech realm.

Imagine you're not just learning about the latest trends, but actively applying them to a real-world project that could transform industries. Intrigued? Let's talk about migrating to version 4 of the Node.js programming model for Azure Functions, and we're doing it in style with a remarkable project: Contoso Real Estate.

This isn't just another tutorial; this is your chance to be part of something bigger. Brace yourselves as we uncover the magic of version 4 and its game-changing potential. But wait, there's more! This knowledge aligns seamlessly with the Hack Together Program: JavaScript on Azure Global Hack – a platform that celebrates innovation, collaboration, and pushing the boundaries of what's possible.

You're not just learning lines of code; you're shaping the future, one function at a time. Imagine the thrill of contributing to projects that might just redefine how we perceive real estate, how we interact with technology, and how we elevate user experiences.

Still curious? Excited? I know I am! Join us for the Hack Together: JavaScript on Azure Global Hack, an event that's not just a date on the calendar but a chance to immerse yourself in a world of innovation. From August 16 to 31, 2023, mark your presence in a community of like-minded developers, thinkers, and creators who dare to dream big.

So, are you ready to turn your curiosity into code, your ideas into impact, and your skills into success? Let's unlock the doors to version 4, amplify our abilities, and make Contoso Real Estate more than just a project – let's make it a testament to what student entrepreneurial developers like you can achieve.

Get ready to code, collaborate, and conquer. The future of tech is in your hands, and together, we're about to redefine what's possible.

Stay curious, stay innovative, and let's Hack Together!

To learn more about:

1. What is Azure Functions v4 for Node?

Hi, friends! Are you curious about Azure Functions v4 for Node? Let me tell you all about it!

Azure Functions v4 is the latest version of the Node.js programming model for Azure Functions. It comes with a bunch of new features and improvements, such as:

  • Flexible folder structure
  • Being able to define function.json directly in the function's in the code
  • New HTTP trigger types
  • Improved IntelliSense
  • Timer Trigger (TypeScript)
  • Durable Functions (TypeScript)

Migrating from v3 to v4 is necessary to take advantage of these new features and improvements as well as the new features of latest versions of the Node.js runtime. However, it's important to note that v4 is still in preview. It is not yet recommended for production use and should be tested for correct behavior in development environments.

Learn more:

2. Create a new Azure Functions v4 app

Microsoft offers a comprehensive guide to help migrate from v3 to v4 complete with tips and best practices to help you with the migration process, covering topics like:

  • Some important considerations
  • Requirements to make the migration
  • How to enable the v4 programming model
  • How to
  • Include the npm package
  • And set up the app entry point
  • How to define the function in code
  • The new usage of context
  • Review the usage of HTTP types

Let's take dive in and see that process with a real app! Let's try this with our Contoso Real Estate Reference Sample

3. About Contoso Real Estate Reference Application

The Contoso Real Estate Reference App is a sample application that demonstrates how to build a real estate website using Azure services in a composable architecture fashion. The app is constructed using a variety of technologies, including TypeScript, Node.js, and the frameworks Angular, Next.js and payment technologies like Stripe. The app includes features such as property listings, property details, and property search. The app also includes an admin portal for managing properties and users.

We then use a selected set of Azure serverless services to deploy to and run the app. Azure Functions plays a key role in the Contoso Real Estate Reference App by providing the backend APIs. The app uses an OpenAPI spec to define the API, and Azure Functions are used to implement the API endpoints. One benefit of using Azure Functions for the API is that it provides fully managed serverless capabilties, which means that you only pay for the compute resources that you use. One challenge of using Azure Functions for the API is that you need to ensure that your functions are secure and scalable.

Learn more:

4. Let's Migrate The App to Azure Functions v4.

1️⃣ | 👉🏽 Identify the target function(s)

For this article, we will migrate only one function from the Contoso Real Estate API to version 4, as it would not be possible to migrate all of them in a single article. The project has has several functions like this -- if you want to see the whole migration process end-to-end, bookmark and watch my Contoso Real Estate API YouTube playlist.

  • This has a series of 10 videos that cover migration of all functions
  • The videos are in Portuguese but should give you a frame of reference to follow along.

The function migration we will be covering in this article is get-users

Screen-Shot-08-07-23-at-07-41-PM.png

note: I made 10 videos (in Portuguese) explaining how I did the entire project API. If you want to watch, just access the playlist: Contoso Real Estate API.

2️⃣ | 👉🏽 Create a new branch in project

Let's use Codespaces! Since it comes with the default development environment pre-defined, it is easier and faster to jumpstart our development without having to set things up ourselves.

note: Codespaces offers free 60 hours per month, renewed on a monthly basis. Whenever you stop using the resource, you must turn off Codespaces to avoid unwanted consumption.

I created a development environment for this migration using Codespaces and consequently created a new branch called GL/content-v3-to-v4. If you want to do the same, feel free. And now inside the packages folder create a folder called api-v4.

Let's go to the steps to migrate the function.

  1. To follow this migration, I will be using the Visual Studio Code extension: Azure Functions. This extension allows us to create an Azure Functions project quickly and easily. To do this, just click on the Azure Functions icon in the Visual Studio Code sidebar and click the Create New Project button.

  2. Then click on: Browse and select the api-v4 folder that we created earlier.

  3. Choose the option: TypeScript

  4. Choose the option: Model V4 (Preview)

  5. Choose the option: HTTP trigger and consequently name the trigger as: users

If you want to follow the step by step, just see the gif below:

v4-function-users.gif

3️⃣ | 👉🏽 Create a new folder for v4 refactor

Once you have created the project, note in the api-v4 folder the new folder structure of the new Node.js programming model for Azure Functions.

Screen-Shot-08-07-23-at-08-21-PM.png

Notice that we now have a folder called src and inside it we have a folder called functions and inside it we have the file called users.ts. This file is our HTTP trigger that we created earlier.

Much less 'polluted' than version 3, isn't it? In which each function had its own folder and inside it had the index.ts file and the function.json.

Did you notice another detail? The function.json file no longer exists. Now, we can define using app.http or app.get within our code.

  • src/functions/index.ts
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";

export async function users(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    context.log(`Http function processed request for url "${request.url}"`);

    const name = request.query.get('name') || await request.text() || 'world';

    return { body: `Hello, ${name}!` };
};

app.http('users', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: users
});
Enter fullscreen mode Exit fullscreen mode

Also note the package.json file:

  • package.json
{
  "name": "api-v4",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "clean": "rimraf dist",
    "prestart": "npm run clean && npm run build",
    "start": "func start",
    "test": "echo \"No tests yet...\""
  },
  "dependencies": {
    "@azure/functions": "^4.0.0-alpha.7"
  },
  "devDependencies": {
    "@types/node": "^18.x",
    "typescript": "^4.0.0",
    "rimraf": "^5.0.0"
  },
  "main": "dist/src/functions/*.js"
}
Enter fullscreen mode Exit fullscreen mode

Please, note that the @azure/functions package is in version 4.0.0-alpha.7. This is because version 4 is still in preview. And, as we are using TypeScript, there is a property called main that points to the *.js file inside the dist/src/functions folder. Later we will make a small change to this file.

To test and see if the function is working, just run the command: npm start, Codespaces will ask you to open the browser. At the end of the URL put: /api/users. If the phrase: Hello World appears, it is because it is working correctly.

See the step by step in the gif below:

execution-function-v4.gif

4️⃣ | 👉🏽 Migration get-users (v3 function) to users (v4)

note: the Contoso Real Estate project API makes use of Azure Cosmos DB. If you don't know, I recommend that you read the documentation. It is a globally distributed, multi-model, NoSQL database and multi-API. In this case here, the MongoDB API is being used and consequently the ORM Mongoose. Let's assume that you have already copied the files related to the database for your migration of the new version of the Azure Functions programming model and that we are only now migrating the functions, okay?

Now that we have the project structure, let's migrate the get-users (v3) function to users (v4). Open the users.ts file and let's start the migration.

  • src/functions/users.ts
import { HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
import { findUsers } from "../models/user";

// GET: All users
export async function getUsers(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
  context.log(`Http function getUsers processed request for url "${request.url}"`);

  const offset = Number(request.query.get("offset")) || 0;
  const limit = Number(request.query.get("limit")) || 10;

  if (offset < 0) {
    return {
      status: 400,
      jsonBody: {
        error: "Offset must be greater than or equal to 0",
      },
    };
  } else if (limit < 0) {
    return {
      status: 400,
      jsonBody: {
        error: "Limit must be greater than or equal to 0",
      },
    };
  } else if (offset > limit) {
    return {
      status: 400,
      jsonBody: {
        error: "Offset must be less than or equal to limit",
      },
    };
  }

  try {
    const users = await findUsers({ offset, limit });

    if (users) {
      return {
        jsonBody: users,
      };
    } else {
      return {
        status: 404,
        jsonBody: {
          error: "Users not found",
        },
      };
    }
  } catch (error) {
    return {
      status: 500,
      jsonBody: {
        error: "Internal Server Error",
      },
    };
  }
};

app.http('users', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: getUsers
});
Enter fullscreen mode Exit fullscreen mode

Probably you will see an error in app.http, because I intentionally removed the call from app at the beginning of the file. Now comes a small change. To make the project more organized, let's create a file inside the src folder called: index.ts. This file will be responsible for importing all the functions of the project and exporting to the main ofpackage.json.

Remove the app.http block contained in the users.ts file and paste it into the index.ts file. Then, import the getUsers function and export it.

  • src/index.ts
app.http('users', {
  methods: ['GET', 'POST'],
  authLevel: 'anonymous',
  handler: getUsers
});
Enter fullscreen mode Exit fullscreen mode
  • src/index.ts
import { app } from "@azure/functions";
import { getUsers } from "./functions/users";

app.get('get-users', {
  route: 'users',
  authLevel: 'anonymous',
  handler: getUsers
});
Enter fullscreen mode Exit fullscreen mode

Notice that, instead of using app.http, we use app.get. This is because, this method specifically will be handling the HTTP verb GET.

In the new Azure Functions programming model, the HTTP verbs are:

  • app.get
  • app.post
  • app.put
  • app.patch
  • app.deleteRequest

And, finally go to the package.json file and change the main property to: dist/src/index.js.

If you want to create a POST, for example, just use the app.post method and so on. And, there in the users.ts file just include the logic related to the HTTP verb POST.

What does this mean? The project structure will be more: concise, simple and very similar to what is already used in Express.js.

Now let's run the project! Run the command: npm start and open the browser. At the end of the URL put: /api/users. In this case, as I am not with the database, it will return an error. But, if you have the database, it will return the registered users or according to the API you will be using.

final-execution-v4.gif

If you want to see the entire project's api migration, there is an open Pull Request to see the migrated apis.

Conclusion

🎉 Congratulations!

We just migrated a v3 Azure Functions app to the new v4 Functions programming model for Node.js, in a few simple steps.

Let's summarize what we learned today:

  • the key differences between Azure Functions v3 and v4 programming models for Node.js
  • how to create a new Azure Functions v4 app from scratch
  • the new model is simpler and more concise (no need for a file per function!)

The specific file changes for this migration can be found in this Pull Request on the Contoso Real Estate application repository. Browse it to get an understanding of the design decisions made and challenges faced when trying to migration a complex project at scale!


Contributions Welcome

  • And, feel free to make contributions to the project. There is a list of good first issues** that you can contribute to.

GitHub logo Azure-Samples / contoso-real-estate

Enterprise-grade Reference Architecture for JavaScript

Enterprise-grade Reference Architecture for JavaScript

This repository contains the reference architecture and components for building enterprise-grade modern composable frontends (or micro-frontends) and cloud-native applications. It is a collection of best practices, architecture patterns, and functional components that can be used to build and deploy modern JavaScript applications to Azure.

Open in GitHub Codespaces

Table of Contents

You can navigate through the documentation using the table of contents below:

Architecture Diagram

Application architecture diagram

Simplified Flow Diagram


<div class="js-render-enrichment-target" data-json='{"data":"flowchart TD\n %% \n\n subgraph Internet\n Portal[https://portal.contoso.com]\n Blog[https://blog.contoso.com]\n CMS[https://cms.contoso.com]\n Stripe[https://stripe.contoso.com]\n API[https://api.contoso.com]\n end\n \n subgraph Azure API Management\n APIM(API Gateway)\n end\n \n subgraph Azure Static Web Apps\n SWA_Angular([Angular])\n end \n\n subgraph Azure Functions\n Functions([Node.js])\n end \n\n subgraph Azure Container Apps\n ACA_Next([Next.js])\n ACA_Strapi([Strapi])\n ACA_Stripe([Stripe])\n end \n\n subgraph Database/Storage\n DB_PostresSQL[(PostgreSQL - Strapi)]\n DB_Mongo[(MongoDB - Portal)]\n Storage([Azure Blob Storage - CMS])\n end \n\n Portal --&gt; SWA_Angular -- \"portal.contoso.com/api/\" --&gt; APIM -- \"portal.contoso.com/api/\" --&gt; Functions\n \n Blog -- \"blog.contoso.com\" --&gt; ACA_Next -. \"Strapi API\" .-&gt; ACA_Strapi\n \n CMS -- \"cms.contoso.com\" --&gt; ACA_Strapi\n ACA_Strapi -- \"read/write\" ----&gt; DB_PostresSQL -- \"read only\" --&gt; Functions\n ACA_Strapi -- \"upload\" --&gt; Storage\n \n API --&gt; APIM -- \"api.contoso.com\" --&gt; Functions &lt;-- \"read/write\" --&gt; DB_Mongo\n\n Stripe ---&gt; APIM -- \"stripe.contoso.com\" --&gt; ACA_Stripe &lt;-. \"validate payment (through APIM)\" .-&gt; Functions\n \n %% Portal\n linkStyle 0 stroke:pink\n linkStyle 1 stroke:pink\n linkStyle 2 stroke:pink\n\n %% Blog\n linkStyle 3 stroke:blue\n linkStyle 4 stroke:blue\n linkStyle 5 stroke:blue\n \n %% CMS\n linkStyle 5 stroke:red\n linkStyle 6 stroke:red\n linkStyle 8 stroke:red\n \n linkStyle 7 stroke:lime\n linkStyle 9 stroke:lime\n linkStyle 10 stroke:lime\n linkStyle 11 stroke:lime\n"}' data-plain='flowchart TD
%%
subgraph Internet
Portal[https://portal.contoso.com]
Blog[https://blog.contoso.com]
CMS[https://cms.contoso.com]
Stripe[https://stripe.contoso.com]
API[https://api.contoso.com]
end
subgraph Azure API Management
APIM(API Gateway)
end
subgraph Azure Static Web Apps
SWA_Angular([Angular])
end
subgraph Azure Functions
Functions([Node.js])
end

subgraph Azure Container Apps
ACA_Next([Next.js])
ACA_Strapi([Strapi])
ACA_Stripe([Stripe])
end 

subgraph Database/Storage
DB_PostresSQL[(PostgreSQL - Strapi)]
DB_Mongo[(MongoDB - Portal)]
Storage([Azure Blob Storage - CMS])
end 

Portal --&gt; SWA_Angular -- "portal.contoso.com/api/**" --&gt; APIM -- "portal.contoso.com/api/**" --&gt; Functions

Blog -- "blog.contoso.com" --&gt; ACA_Next -. "Strapi API" .-&gt; ACA_Strapi

CMS -- "cms.contoso.com" --&gt; ACA_Strapi
ACA_Strapi -- "read/write" ----&gt; DB_PostresSQL -- "read only" --&gt; Functions
ACA_Strapi -- "upload" --&gt; Storage

API --&gt; APIM -- "api.contoso.com" --&gt; Functions &lt;-- "read/write" --&gt; DB_Mongo

Stripe ---&gt; APIM -- "stripe.contoso.com" --&gt; ACA_Stripe &lt;-. "validate payment (through APIM)" .-&gt; Functions

%% Portal
linkStyle 0 stroke:pink
linkStyle 1 stroke:pink
linkStyle 2 stroke:pink

%% Blog
linkStyle 3 stroke:blue
linkStyle 4 stroke:blue
linkStyle 5 stroke:blue

%% CMS
linkStyle 5 stroke:red
linkStyle 6 stroke:red
linkStyle 8 stroke:red

linkStyle 7 stroke:lime
linkStyle 9 stroke:lime
linkStyle 10 stroke:lime
linkStyle 11 stroke:lime
Enter fullscreen mode Exit fullscreen mode

' dir="auto">


flowchart TD
%%
subgraph Internet
Portal[https://portal.contoso.com]
Blog[https://blog.contoso.com]
CMS[https://cms.contoso.com]
Stripe[https://stripe.contoso.com]
API[https://api.contoso.com]
end
subgraph Azure API Management
APIM(API Gateway)
end
subgraph Azure Static Web Apps
SWA_Angular([Angular])
end 

subgraph Azure Functions
Functions([Node.js])
end 

subgraph Azure Container Apps
ACA_Next([Next.js])
ACA_Strapi([Strapi])
ACA_Stripe([Stripe])
end 

subgraph Database/Storage
DB_PostresSQL[(PostgreSQL</pre>…</div>
Enter fullscreen mode Exit fullscreen mode




I hope you enjoyed it and see you next time!

And, happy coding! 🚀🚀🚀

Top comments (0)