1. Verify if Azure CLI is Installed
Run the following command to check if Azure CLI is installed:
which az
If nothing is returned, the CLI is not installed. Proceed to install it.
2. Install Azure CLI
On macOS (using Homebrew)
If you’re on macOS, install Azure CLI using Homebrew:
brew update && brew install azure-cli
If you don’t have Homebrew installed, first run:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
On Linux
Follow the official instructions here: Install Azure CLI on Linux.
On Windows
You can install Azure CLI using the MSI installer from the official website: Install Azure CLI.
Starting your CRUD in Azure Functions
To begin creating your product CRUD with Azure Functions, you’ll first need to set up your environment and execute specific commands in the terminal. Make sure you have the Azure CLI, Azure Functions Core Tools, and the appropriate runtime (e.g., Node.js) installed.
Start by logging into Azure. Run the following command to authenticate your account:
az login
This will open a browser where you can log in. After authentication, navigate to the directory where you want to create your project. Create a folder for your CRUD application and move into it:
mkdir azure-functions-samplecd azure-functions-sample
Azure Functions Runtime
In this example, we will be using Node.js as the Azure Functions runtime.
If you do not have node installed, install it first. If you already have it, skip this step.
First, ensure you install a Node.js version compatible with Azure Functions. As of the time of writing, the recommended version is Node.js 18.
On Mac
Using Homebrew: Open Terminal and run:
brew install node@18
echo 'export PATH="/opt/homebrew/opt/node@18/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
Verify installation:
node -v npm -v
On Windows
Download the installer:
Visit Node.js Downloads.
Choose the LTS version and download the .msi
file.
Run the installer:
Follow the installation prompts and ensure Add to PATH
is selected.
Verify installation: Open Command Prompt and run:
node -v npm -v
On Linux
Using NodeSource (recommended for most distros):
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - sudo apt-get install -y nodejs
This works for Ubuntu/Debian. For other distros, check NodeSource.
Verify installation:
node -v npm -v
Initializing the project
Initialize the Azure Functions project with the desired runtime. For example, if you’re using Node.js, use the following command:
func init azure-functions-sample --worker-runtime node
This creates the project structure. To add functions for your CRUD operations, start with the func new
command. When prompted, select the HTTP Trigger template and name your function (e.g., CreateProduct
). Repeat this step for other operations like reading, updating, and deleting products.
For instance, create the function:
func new
For node choose option 3: node.
For language you can choose javascript or typescript. In this example we’ll choose 2: typescript.
Choose option 8 for the HTTP trigger template, and name it “product”.
Build and run locally
To be able to run the project, first you must build it:
npm run build
Then you can alreay run the project:
func start
Or you can use the following command to do both:
npm run build & func start
The product API
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
type Product = {
id: string;
name: string;
price: number;
};
const products: Record<string, Product> = {};
export async function product(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
context.log(`HTTP function processed request for url "${request.url}"`);
const method = request.method.toUpperCase();
const productId = request.query.get("id");
switch (method) {
case "GET": {
if (productId) {
const product = products[productId];
if (product) {
return { body: JSON.stringify({ success: true, data: product }), status: 200 };
} else {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
} else {
return { body: JSON.stringify({ success: true, data: Object.values(products) }), status: 200 };
}
}
case "POST": {
const { id, name, price } = (await request.json()) as Product;
if (!id || !name || !price) {
return { body: JSON.stringify({ success: false, message: "Invalid product data. 'id', 'name', and 'price' are required." }), status: 400 };
}
products[id] = { id, name, price };
return { body: JSON.stringify({ success: true, message: `Product with ID "${id}" created.` }), status: 201 };
}
case "PUT": {
if (!productId) {
return { body: JSON.stringify({ success: false, message: "Product ID is required for updating." }), status: 400 };
}
const { name, price } = (await request.json()) as Partial<Product>;
const product = products[productId];
if (!product) {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
if (name) product.name = name;
if (price) product.price = price;
return { body: JSON.stringify({ success: true, message: `Product with ID "${productId}" updated.` }), status: 200 };
}
case "DELETE": {
if (!productId) {
return { body: JSON.stringify({ success: false, message: "Product ID is required for deletion." }), status: 400 };
}
const product = products[productId];
if (!product) {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
delete products[productId];
return { body: JSON.stringify({ success: true, message: `Product with ID "${productId}" deleted.` }), status: 200 };
}
default: {
return { body: JSON.stringify({ success: false, message: `Method ${method} not allowed.` }), status: 405 };
}
}
}
app.http('product', {
methods: ['GET', 'POST', 'PUT', 'DELETE'],
authLevel: 'anonymous',
handler: product
});
This code implements an Azure Function that provides a basic CRUD (Create, Read, Update, Delete) functionality for managing products. Instead of using a database, it relies on a simple in-memory array (products
) for storing data, making it suitable for tutorials or initial learning purposes without introducing database complexity.
How It Works
1. Data Storage
The products
variable is a simple JavaScript object (a dictionary) used to simulate a database. Each product is stored as a key-value pair, where the id
acts as the key and the product data is the value.
const products: Record<string, Product> = {};
2. HTTP Methods
The function supports four HTTP methods:
- GET: Fetches products (all or by
id
). - POST: Creates a new product.
- PUT: Updates an existing product.
- DELETE: Deletes a product by
id
.
Code Breakdown
The code provides a simple CRUD API for managing products using an in-memory object as the data store. It supports four HTTP methods: GET, POST, PUT, and DELETE. Each method performs specific operations and responds with structured JSON, ensuring clarity and consistency.
GET Method
The GET
method retrieves one or all products. If an id
is specified in the query string, the function fetches the product with that ID. If no id
is provided, it returns all products.
Code:
case "GET": {
if (productId) {
const product = products[productId];
if (product) {
return { body: JSON.stringify({ success: true, data: product }), status: 200 };
} else {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
} else {
return { body: JSON.stringify({ success: true, data: Object.values(products) }), status: 200 };
}
}
Example Requests:
Fetch all products:
GET /api/product{ "success": true, "data": [ { "id": "1", "name": "Product A", "price": 10.0 }, { "id": "2", "name": "Product B", "price": 15.0 } ] }
Fetch a single product:
GET /api/product?id=1{ "success": true, "data": { "id": "1", "name": "Product A", "price": 10.0 } }
POST Method
The POST
method creates a new product. The request must include id
, name
, and price
in the JSON body. If any required field is missing, an error is returned.
Code:
case "POST": {
const { id, name, price } = (await request.json()) as Product;
if (!id || !name || !price) {
return { body: JSON.stringify({ success: false, message: "Invalid product data. 'id', 'name', and 'price' are required." }), status: 400 };
}
products[id] = { id, name, price };
return { body: JSON.stringify({ success: true, message: `Product with ID "${id}" created.` }), status: 201 };
}
Example Requests:
Create a product:
POST /api/product
Body:{ "id": "3", "name": "Product C", "price": 20.0 }
Response:{ "success": true, "message": "Product with ID '3' created." }
Error example:
Body:{ "name": "Product D" }
Response:{ "success": false, "message": "Invalid product data. 'id', 'name', and 'price' are required." }
PUT Method
The PUT
method updates an existing product. The product ID must be specified in the query string, and the updated details must be included in the request body. If the product does not exist, an error is returned.
Code:
case "PUT": {
if (!productId) {
return { body: JSON.stringify({ success: false, message: "Product ID is required for updating." }), status: 400 };
}
const { name, price } = (await request.json()) as Partial<Product>;
const product = products[productId];
if (!product) {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
if (name) product.name = name;
if (price) product.price = price;
return { body: JSON.stringify({ success: true, message: `Product with ID "${productId}" updated.` }), status: 200 };
}
Example Requests:
Update a product:
PUT /api/product?id=1
Body:{ "name": "Updated Product A", "price": 12.0 }
Response:{ "success": true, "message": "Product with ID '1' updated." }
Error example:
PUT /api/product?id=999
Response:
{ "success": false, "message": "Product with ID '999' not found." }
DELETE Method
The DELETE
method removes a product based on its ID. If the product does not exist, an error is returned.
Code:
case "DELETE": {
if (!productId) {
return { body: JSON.stringify({ success: false, message: "Product ID is required for deletion." }), status: 400 };
}
const product = products[productId];
if (!product) {
return { body: JSON.stringify({ success: false, message: `Product with ID "${productId}" not found.` }), status: 404 };
}
delete products[productId];
return { body: JSON.stringify({ success: true, message: `Product with ID "${productId}" deleted.` }), status: 200 };
}
Example Requests:
Delete a product:
DELETE /api/product?id=1{ "success": true, "message": "Product with ID '1' deleted." }
Error example:
DELETE /api/product?id=999{ "success": false, "message": "Product with ID '999' not found." }
This structure ensures the API is easy to use, with clear error handling and consistent JSON responses for all methods.
Why Use an Array?
This approach simplifies the setup by avoiding database dependencies. The products
object is stored in memory, meaning all data will be lost when the function restarts. This is ideal for tutorials, testing, and scenarios where you want to focus on the basics of Azure Functions.
Next Steps
Once you’re comfortable with this, you can extend the implementation to connect to a database like Azure Cosmos DB, SQL, or MongoDB to persist data across sessions.