Share on facebook
Share on twitter
Share on linkedin

How to create CRUD applications with Azure Functions and MongoDB

Glaucia Lemos
Glaucia Lemos

In this article, we’ll be learning how we can make a CRUD application with Azure Functions and integrated with MongoDB and Node.js

You can develop Serverless applications with Azure Functions in loads of programming languages: C#, JavaScript, F#, Java, PowerShell, Python, TypeScript, and other languages such as Go, Rust & R. However, for this post we’ll focus on JavaScript. Let’s get started!

1. Install Azure Functions Core Tools package

Azure Functions Core Tools will allow us to develop and test functions locally on our machine from a terminal or command prompt. Here are the links to programs and packages I used to solve this challenge:

The following assumes that version X of Node.js is installed on the machine.

  • Windows
npm install -g azure-functions-core-tools
  • MacOS
brew tap azure/functions
brew install azure-functions-core-tools
  • Linux (Ubuntu/Debian) with APT
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg

For more information on how to correctly install Azure Functions Core Tools, visit the link HERE.

To verify that Azure Functions Core Tools is installed correctly on your machine, check for the func command on the terminal:

> func

If it happens according to the GIF below, it’s because the package was installed successfully!

Great. Now we can create our functions. To do this, create a local folder on your machine — and let’s get started!

2. Begin creating your new application

Now that we have installed the package, let’s create a new application. For this, just follow the steps as the GIF below.

Note that when we open Visual Studio Code, we need to click the YES button that appears in the bottom right corner to enable some important files in the project.

3. Create the MongoDB connection

Now let’s make some necessary changes to our newly created project. With this, we will install MongoDB locally in our project. Enter the command:

npm install mongodb

When we installed MongoDB in the project, note that there were changes in the package.json file. The file will look as below:

  • file: package.json
{
  "name": "crud-serverless-mongodb",
  "version": "1.0.0",
  "description": "Challenge-4 25 days of serverless",
  "scripts": {
    "test": "echo \"No tests yet...\""
  },
  "author": "",
  "dependencies": {
    "mongodb": "^3.3.2"
  }
}

Now let’s create a folder called: shared and inside it we will create the file: mongo.js. The project structure will now as you can see below:

  • file: shared/mongo.js
/**
 * Arquivo: mongo.js
 * Data: 01/25/2021
 * Descrição: file responsible for handling the database connection locally
 * Author: Glaucia Lemos – (Twitter: @glaucia_lemos86)
 */

const { MongoClient } = require("mongodb");

const config = {
  url: "mongodb://localhost:27017/crud-serverless-mongodb",
  dbName: "crud-serverless-mongodb"
};

async function createConnection() {
  const connection = await MongoClient.connect(config.url, {
    useNewUrlParser: true
  });
  const db = connection.db(config.dbName);
  return {
    connection,
    db
  };
}

module.exports = createConnection;

Here we are creating our local connection to MongoDB!

And let’s also change the local.settings.jsonfile. This file is responsible for “storing” all keys that we do not want to be exposed when commit to GitHub. Note that this file is in the file list in .gitignore.

Open the local.settings.json file and make some changes:

  • file: local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "{AzureWebJobsStorage}"
  },
  "Host": {
    "LocalHttpPort": 7071,
    "CORS": "*"
  }
}

See in the code above that we are already enabling CORS. Because without it, we can’t perform CRUD operations! If you’d like to understand a little bit more about CORS, I recommend this reading HERE.

Great! The first step is already finished it. Now let’s create our CRUD in Azure Functions!


How to build a Serverless app using Go and Azure Functions
With Azure Functions Custom Handlers, you can use Go for serverless functions on Azure. Read our how-to guide on using Custom Handlers.

4. Function – ‘CreateDish’

To create a new function just type the following command:

func new

When you enter this command, it will give you several template options that Azure Functions makes available to us. Let’s choose HttpTrigger template.

Note that a CreateDish folder and two files were created:

  • function.json: here we’ll define the routes and endpoint methods.
  • index.json: here we’ll develop endpoint logic.

Let’s start to change these files. Starting with function.json

  • file: CreateDish/function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["post"],
      "route": "dishes"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

Now let’s change the index.js file:

  • file: CreateDish/index.js
/**
 * File: CreateDish/index.js
 * Description: file responsible for creating a new 'Dish'
 * Date: 01/25/2021
 * Author: Glaucia Lemos (Twitter: @glaucia_lemos86)
 */

const createMongoClient = require('../shared/mongo');

module.exports = async function (context, req) {
  const dish= req.body || {}

  if (dish) {
    context.res = {
      status: 400,
      body: 'Dish data is required! '
    }
  }

  const { db, connection } = await createMongoClient()

  const Dishes = db.collection('dishes')

  try {
    const dishes = await Dishes.insert(dish)
    connection.close()

    context.res = {
      status: 201,
      body: dishes.ops[0]
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Error creating a new Dish'
    }
  }
}

Here we’re defining the Post route and developing the logic for: Create a New Dish.

Let’s run this endpoint! To run, just type the command:

func host start

And it will list our created endpoint! Look at the GIF below.

It lists for us the following endpoint: [POST] http://localhost:7071/api/dishes

5. Function – ‘GetAllDishes’

It’s the same thing we did above. Let’s create a new function with the command: func new, include the the function name as GetAllDishes and change the files: function.json and index.js

  • GetAllDishes/function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get"],
      "route": "dishes"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
  • GetAllDishes/index.js
/**
 * File: GetAllDishes/index.js
 * Description: file responsible for list all 'Dishes'
 * Data: 01/25/2021
 * Author: Glaucia Lemos (Twitter: @glaucia_lemos86)
 */

const createMongoClient = require('../shared/mongo')

module.exports = async context => {
  const { db, connection } = await createMongoClient()

  const Dishes = db.collection('dishes')
  const res = await Dishes.find({})
  const body = await res.toArray()

  connection.close()

  context.res = {
    status: 200,
    body
  }
}

6. Function – ‘GetDishById’

Now that it’s clear how easy it is to create a CRUD with Azure Functions, I’ll start speeding up the creation process and just inform you what has changed in the function.json and index.js files:

  • GetDishById/function.json
  {
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get"],
      "route": "dishes/{id}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
  • GetDishById/index.js
// @ts-nocheck
/**
 * File: GetDishById/index.js
 * Description: file responsible for get a 'Dish' by Id
 * Data: 01/25/2021
 * Author: Glaucia Lemos (@glaucia_lemos86)
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params

  if (!id) {
    context.res = {
      status: 400,
      body: 'Please enter the correct Dish Id number!'
    }

    return
  }

  const { db, connection } = await createMongoClient()

  const Dishes = db.collection('dishes')

  try {
    const body = await Dishes.findOne({ _id: ObjectID(id) })

    connection.close()
    context.res = {
      status: 200,
      body
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Error listing Dish by Id.'
    }
  }
}

7. Function – ‘UpdateDishById’

  • UpdateDishById/function.json
{
  "bindings": [{
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": ["put"],
          "route": "dishes/{id}"
      },
      {
          "type": "http",
          "direction": "out",
          "name": "res"
      }
  ]
}
  • UpdateDishById/index.js
// @ts-nocheck
/**
 * File: UpdateDishById/index.js
 * Description: file responsible for update a 'Dish' by Id
 * Data: 01/25/2021
 * Author: Glaucia Lemos (@glaucia_lemos86)
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params
  const dish = req.body || {}

  if (!id || !dish) {
    context.res = {
      status: 400,
      body: 'Fields are required'
    }

    return
  }

  const { db, connection } = await createMongoClient()
  const Dishes = db.collection('dishes')

  try {
    const dishes = await Dishes.findOneAndUpdate(
      { _id: ObjectID(id) },
      { $set: dish }
    )

    connection.close()

    context.res = {
      status: 200,
      body: dishes
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Error updating a Dish'
    }
  }
}

8. Function -‘DeleteDishById’

Above the ‘DeleteBishById code:

  • DeleteDishById/function.json
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["delete"],
      "route": "dishes/{id}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
  • DeleteDishById/index.js
// @ts-nocheck
/**
 * File: DeleteDishById/index.js
 * Description: file responsible for delete a 'Dish' by Id
 * Data: 01/25/2021
 * Author: Glaucia Lemos (Twitter: @glaucia_lemos86)
 */

const { ObjectID } = require('mongodb')
const createMongoClient = require('../shared/mongo')

module.exports = async function (context, req) {
  const { id } = req.params

  if (!id) {
    context.res = {
      status: 400,
      body: 'The fields are required!'
    }

    return
  }

  const { db, connection } = await createMongoClient()

  const Dishes = db.collection('dishes')

  try {
    await Dishes.findOneAndDelete({ _id: ObjectID(id) })
    connection.close()
    context.res = {
      status: 204,
      body: 'Dish deleted successfully!'
    }
  } catch (error) {
    context.res = {
      status: 500,
      body: 'Error Deleting Dish' + id
    }
  }
}

And our CRUD is ready! Let’s test all endpoints! To test, open Postman, you can download POSTMAN – HERE and include the json request. See the example below:

{
  "name": "Amanda",
  "dish": "garlicky green beans",
  "vegetarian": true,
  "vegan": false,
  "allergens": "nuts (almonds)"
}
  • Create a new dish: [POST] http://localhost:7071/api/dishes/
  • List all dishes: [GET] http://localhost:7071/api/dishes/
  • List dishes by Id: [GET] http://localhost:7071/api/dishes/{id}
  • Update dish by Id: [PUT] http://localhost:7071/api/dishes/{id}
  • Delete dish by Id: [DELETE] http://localhost:7071/api/dishes/{id}

Learn more about Azure Fundamentals

If you want to learn more about Azure Functions, Microsoft offers free courses and eBooks to help you to learn more about serverless and Azure Functions:

And if you want to know more about other technology news, Azure and JavaScript / Node.Js, be sure to follow me on Twitter!

 

 

Recommended

Get more insights, news, and assorted awesomeness around all things cloud learning.

Get Started
Who’s going to be learning?
Sign In
Welcome Back!
Thanks for reaching out!

You’ll hear from us shortly. In the meantime, why not check out what our customers have to say about ACG?

How many seats do you need?

  • $499 USD per seat per year
  • Billed Annually
  • Renews in 12 months

Ready to accelerate learning?

For over 25 licenses, a member of our sales team will walk you through a custom tailored solution for your business.


$2,495.00

Checkout