How to create a custom connector for your own Azure hosted API
In this blog post I am going to cover
- How to create an API with JavaScript
- How to deploy this to Azure
- How to wrap the API into a custom connector
- How to use the connector in Power Apps
Don’t know what is an API? Don’t worry, here is help:
Imagine, you are dining in a restaurant (this is the app/client). You do not talk to the kitchen (server/database) directly, but you can send requests to the API, which is our waiter*waitress. They will request dishes from the kitchen and get them in return and then respond back to you.
Operations you could do are
- GET (request data from a server)
- POST (send new information to a server)
- PUT (make changes to existing data on a server by replacing the entire entity)
- PATCH (make changes to existing data on a server by updating provided fields)
- DELETE (remove existing information from a server)
How to approach things
In this example, we only want to do GET requests, as we want to read news from different newspaper websites such as BBC, New York Times and New York Post. In order to achieve that, we will scrape the websites of these sources and extract title and URL of the articles. We will later deploy this to Azure as a web app and wrap the API into a custom connector for Power Platform. Last thing is then consuming this custom connector in a Canvas app.
1. make it work on your machine
-
Create a new directory on your computer and navigate into it
-
Make sure you have node.js installed
-
Run
npm init
, which prompts you to answer some questions about your project (you can just hit return). It will then create apackage.json
file in your project for you. It contains some metadata about your app such as your name, the version and the license. You can change the file according to your needs. -
Create a index.js file as an entrypoint for the app
-
Install the following dependencies
π‘ Dependencies will show up in your package.json
file.
Please delete the “scripts” object in this file and replace it by this object:
"scripts": {
"start": "nodemon index.js"
}
We can later start our app with nodemon index.js
π‘ You can also see now, that you have a package-lock.json
file - you may want to have a look, but we won’t touch this file.
We wil now go ahead, open our index.js
file and first create the listener π
const PORT = process.env.PORT || 8000
const axios = require('axios')
const cheerio = require('cheerio')
const express = require('express')
const app = express()
app.listen(PORT, () => console.log(`my server is running on PORT ${PORT} π`))
You can of course use a port of your choice.
We can now create our first endpoint:
app.get('/', (req, res) => {
res.json('Welcome to my π° News API')
})
This endpoint sits at the root /
and displays just a little welcome message. You can later delete that, it’s just to see if things already work.
Now we want to create an array of newspapers/sources that we later want to loop over.
π‘ As some of them don’t provide their baseURL when linking to an article on their website but only a relative path, it’s a good idea to have a baseURL property.
const newspapers = [
{
name: 'nyt',
address: 'https://www.nytimes.com/',
baseURL: ''
},
{
name: 'un',
address: 'https://www.un.org/',
baseURL: ''
},
{
name: 'bbc',
address: 'https://www.bbc.co.uk/news/',
baseURL: 'https://www.bbc.co.uk'
},
{
name: 'nyp',
address: 'https://nypost.com/',
baseURL: ''
}
]
Next step is to create an empty array of articles and then loop over all newspapers to fill the array with articles
- Visit the address using axios,
- Save the response (this is the entire HTML of the website)
- Pass this HTML into cheerio
- Look for elements with an
a
tag that contains “Ukraine” - Grab the title (text) and the url (which us the
href
attribute) - Push this as an object into our articles array
const articles = []
newspapers.forEach(newspaper => {
axios.get(newspaper.address)
.then(response => {
const pageHTML = response.data
const $ = cheerio.load(pageHTML)
$('a:contains("Ukraine")', pageHTML).each(function () {
const title = $(this).text()
const url = $(this).attr('href')
articles.push({
title,
url: newspaper.baseURL + url,
source: newspaper.name
})
})
})
})
We now want to see this work in action - and create an endpoint /news
for it:
app.get('/news', (req, res) => {
res.json(articles)
})
If you now visit localhost:8000/news
,you should see an array of of objects displaying title, source and URL of articles of all your sources. So far, so good. but what if we want to filter the sources with /news/nyt
or /news/bbc
?
Let’s introduce the newspaperId
. We will again do the same axios and cheerio magic once again - Our new array is now called specificArticles
and will also contain the id of the source.
app.get('/news/:newspaperId', (req, res) => {
const newspaperId = req.params.newspaperId
const newspaperAddress = newspapers.filter(newspaper => newspaper.name == newspaperId)[0].address
const newspaperBase = newspapers.filter(newspaper => newspaper.name == newspaperId)[0].base
axios.get(newspaperAddress)
.then(response => {
const pageHTML= response.data
const $ = cheerio.load(pageHTML)
const specificArticles = []
$('a:contains("Ukraine")', pageHTML).each(function () {
const title = $(this).text()
const url = $(this).attr('href')
specificArticles.push({
title,
url: newspaperBase + url,
source: newspaperId
})
})
res.json(specificArticles)
}).catch(err => console.log(err))
})
You can now test this with localhost:8000/news/bbc
or localhost:8000/news/nyt
π Please do yourself a favor and create a .gitignore
file with
node_modules
so that you later on don’t commit the node modules.
2. Make it work on Azure
Cool, we made a service work on our machine, but more people could benefit if we deployed this API now to Azure. The service we will be using here is an Azure Web app. As I am a Windows person, I’d like to try more things on Linux, so we will be creating a Linux app π€
- Open VS Code or the terminal of your choice
- make sure you have Azure CLI installed
- Create a new web app with the following command:
az webapp up --sku F1 --name <mywebapp42> --location <location-name>
π‘ please note:
- the name of the webapp needs to be globally unique
- the location is optional - if you don’t know which region you want to use, you can get a list with
az appservice list-locations --sku F1
- if you don’t like Linux, use
--os-type Windows
The web app will be created for you, this can take a few moments, as it does not only create the web app for you, but also automatically creates a resource group and an App service plan. It also packs and deploys your local project.
In the output, you will see something like:
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 202
You can launch the app at http://<mywebapp42>.azurewebsites.net
Select that link! In case you didn’t delete the root endpoint, you should now see the greeting β€ Test your other endpoints as well! Also go to the Azure portal and check the resources that have been created for you.
It should look something like this:
3. Wrap your API into a custom connector
Now that we have a cool API, let’s use it in Power Platform!
- Open flow.microsoft.com
- Select Data, select Custom Connectors
- Select New
- in the General for Host enter your new app URL
http://<mywebapp42>.azurewebsites.net
- Don’t do anything in the Security tab - this is a public API and we don’t need authenticated access
- On the Definition tab, select New action
- For Summary, Description, and Operation ID enter something like
GetNewsAll
- Select Import from sample for Request
- Select Get and paste in the URL
http://<mywebapp42>.azurewebsites.net/news
- Select Add a default response and paste an array in it - you don’t need actual values, the schema is enough.
[
{
"title": "string",
"url": "string",
"source": "string"
},
{
"title": "string",
"url": "string",
"source": "string"
}
]
- Select Create connector
- you may want to test your connector on the Test tab. To do so, select New Connection and then select Test operation - This should return an HTTP code 200 and you should see some real data.
Repeat the steps 6-12 for the other endpoints that you created (i.e. /news/bbc)
You can also pass in the newspaperId as a parameter.
4. Consume the custom connector in a Canvas app
- Open make.powerapps.com
- Create a new canvas app from blank - don’t forget to save it
- In the left hand bar, select Data and add your custom connector
- Create a gallery and set its Items property to
Defaulttitle.GetNewsAll()
- Set the
- Text property of the Title label of the gallery to
ThisItem.title
, - Text property of the Subtitle label of the gallery to
ThisItem.source
, - Onselect property of the icon of the gallery to
Launch(ThisItem.url)
- Text property of the Title label of the gallery to
You can even create a dropdown menu that holds the different sources so that users can change what they want to consume.
Conclusion
You can wrap ANY API into a custom connector for Power Platform - even the ones that you create.
Resources
Some helpful resources
Quickstart: Create a Node.js web app - Azure App Service | Microsoft Docs How to Scrape Websites with Node.js and Cheerio (freecodecamp.org)
Create a custom connector from scratch | Microsoft Docs
How to use a custom connector in Power Automate (m365princess.com)
Working with Custom Connectors in Power Platform for beginners (michaelroth42.com)
#SharingIsCaring π
Published on:
Learn moreRelated posts
Azure Developer CLI (azd) – November 2024
This post announces the November release of the Azure Developer CLI (`azd`). The post Azure Developer CLI (azd) – November 2024 appeared...
Microsoft Purview | Information Protection: Auto-labeling for Microsoft Azure Storage and Azure SQL
Microsoft Purview | Information Protection will soon offer Auto-labeling for Microsoft Azure Storage and Azure SQL, providing automatic l...
5 Proven Benefits of Moving Legacy Platforms to Azure Databricks
With evolving data demands, many organizations are finding that legacy platforms like Teradata, Hadoop, and Exadata no longer meet their needs...
November Patches for Azure DevOps Server
Today we are releasing patches that impact our self-hosted product, Azure DevOps Server. We strongly encourage and recommend that all customer...
Elevate Your Skills with Azure Cosmos DB: Must-Attend Sessions at Ignite 2024
Calling all Azure Cosmos DB enthusiasts: Join us at Microsoft Ignite 2024 to learn all about how weβre empowering the next wave of AI innovati...
Query rewriting for RAG in Azure AI Search
Getting Started with Bicep: Simplifying Infrastructure as Code on Azure
Bicep is an Infrastructure as Code (IaC) language that allows you to declaratively define Azure resources, enabling automated and repeatable d...
How Azure AI Search powers RAG in ChatGPT and global scale apps
Millions of people use Azure AI Search every day without knowing it. You can enable your apps with the same search that enables retrieval-augm...
Episode 388 – Getting Started with Azure Bicep: Infrastructure as Code with a Domain Specific Language
Welcome to Episode 388 of the Microsoft Cloud IT Pro Podcast. In this episode, we dive into Azure Bicep, Microsoftβs streamlined language for ...