In this guide, we will explore how to query a Postgres database hosted on Neon using Azure Functions. This combination allows you to take advantage of a flexible, high-performance infrastructure without worrying about server management.
Prerequisites
You will need:
- An Azure account with a subscription to deploy Azure Functions.
- A Neon account. If you don’t have one yet, you can sign up.
- Basic knowledge of Node.js and SQL.
- Familiarity with using Visual Studio Code.
Why Neon?
Neon stands out as a cloud-native Postgres solution with an innovative architecture that separates compute and storage, offering a truly serverless database. This means Neon automatically adjusts its resources based on your application’s needs, making it ideal for projects that require flexible scalability without directly managing the infrastructure. In other words, Neon allows you to accelerate project delivery by focusing solely on development, while having an infrastructure that scales on demand.
Neon Database is cloud-native Serverless Postgres, meaning it has completly separated storage from compute. This means Neon automatically adjusts its resources based on your application’s needs, making it ideal for projects that require flexible scaling without directly managing the infrastructure. In other words, Neon Database is a fully managed database service that allows you to focus on building your application without worrying about the underlying infrastructure.
Azure Functions are also a serverless compute service, which enables you to run event-driven code without having to manage infrastructure. It is a great choice for building serverless applications, microservices, and APIs. By combining Neon Database with Azure Functions, you can create a powerful and scalable application that can handle a wide range of use cases.
At the same time, Azure Functions enables you to run code in response to events without worrying about the underlying infrastructure. It will create microservices that respond to events, such as HTTP requests, without the need to deploy or manage servers.
To illustrate this, we will discuss an example of client management (hotel reservation management), which is a common use case in application development. We will use the technologies mentioned above to query and process data.
Context
Imagine you are developing a solution to manage hotel reservations. You want to allow users (via an app or website) to view available reservations and interact with a Postgres database hosted on Neon.
The application's features will include:
- View available rooms: The application will allow users to check available hotel rooms for booking.
- Add a new reservation: When a customer makes a reservation, their information will be stored in the Neon.
- Cancel a reservation: Customers can cancel a reservation by deleting the corresponding record from the database.
Step 1: Create and Configure the Database on Neon
Sign up and create the database
Sign up on Neon and follow the steps to create a Postgres database. The database will be named neondb.
After creating the database, make sure to copy the connection details (such as host, user, password, database) somewhere safe, as they will be used to configure Azure Functions to connect to Neon.
-
Creating the tables
Once the database is created, you should see an option named "SQL Editor" on the left to write and execute queries.
In the query editor, copy and paste the SQL code below to create the
clients
andhotels
tables. These are reference tables, as thereservations
table will refer to these tables via foreign keys:Here is the SQL script to create the
reservations
table: -
Inserting test data
You can insert some example data into the database to ensure that everything is working fine up to this point.
Here is the SQL script to insert data into the
clients
table:Here is the SQL script to insert data into the
hotels
table:Here is the SQL script to insert data into the
reservations
table:
Step 2: Create an Azure Function to Manage Products
-
Sign in to Azure
If you don't already have an account, sign up on the Microsoft Azure portal.
We will initialize an Azure Functions project where we will create an HTTP Trigger function in Visual Studio Code (VS Code) using the Azure Functions extension.
-
Install the Azure Functions extension:
- Open VS Code, or install Visual Studio Code if it's not yet installed.
- Go to the extensions tab or press
Ctrl+Shift+X
. - Search for "Azure Functions" and install the official extension.
-
Create an Azure Functions Project
Open the command palette or press
Ctrl+Shift+P
to open the command palette.- Type
Azure Functions: Create New Project...
and select that option. - Choose a directory where you want to create the project.
- Select the programming language (
JavaScript
in our case). - Choose a JavaScript programming model (
Model V4
). - Choose a function template, and select
HTTP trigger
. - Give your function a name, for example,
manageClients
.
Once confirmed, the project will be created with some default code.
- Type
-
Install the Postgres client
In the terminal of your Azure Functions project, install either Neon serverless driver or the
node-postgres
(pg
) package, which will be used to connect to Postgres: -
Azure Functions Core Tools
Install Azure Functions Core Tools to run functions locally.
npm install -g azure-functions-core-tools@4 --unsafe-perm true
suggested folder structure
Since there are three tables in the database (
Clients
,Hotels
, andReservations
), using a separate file for each feature or interaction with the database is a good practice to maintain clear and organized code.src/ ├──index.js ├──functions/ │ ├──manageClients.js │ ├──manageHotels.js │ └──manageReservations.js └──database/ ├──client.js ├──hotel.js └──reservation.js
-
Configure Environment Variables
On the Neon dashboard, go to
Connection string
, selectNode.js
, and click.env
. Then, clickshow password
and copy the database connection string. If you don't clickshow password
, you'll copy a connection string without the password (which is masked).Create a
.env
file at the root of the project to store your database connection information from the Neon.Here's an example of the connection string you'll copy:
DATABASE_URL='postgresql://neondb_owner:************@ep-quiet-leaf-a85k5wbg.eastus2.azure.neon.tech/neondb?sslmode=require'
-
Modify the
local.settings.json
fileThe
local.settings.json
file is used by Azure Functions for local executions. Azure Functions does not directly read the.env
file. Instead, it relies onlocal.settings.json
to inject environment variable values during local execution. In production, you will define the same settings throughApp Settings
in the Azure portal.Here's an example of the
local.settings.json
file :{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "node", "DATABASE_URL": "postgresql://neondb_owner:************@ep-quiet-leaf-a85k5wbg.eastus2.azure.neon.tech/neondb?sslmode=require" } }
Install the
dotenv
package by opening the terminal in your Azure Functions project. This package will allow you to load environment variables from the.env
file:npm install dotenv
-
Manage Each Table
a. Create a separate file for each table in the
database/
folder.Here, you can use either the
neon
package or thepg
package to connect to the database.Example code for
client.js
import { neon } from '@neondatabase/serverless'; import dotenv from 'dotenv'; dotenv.config(); const sql = neon(process.env.DATABASE_URL); const getAllClients = async () => { const rows = await sql`SELECT * FROM clients`; return rows; }; const addClient = async (first_name, last_name, email, phone_number) => { const [newClient] = await sql` INSERT INTO clients (first_name, last_name, email, phone_number) VALUES (${first_name}, ${last_name}, ${email}, ${phone_number}) RETURNING *`; return newClient; }; export { getAllClients, addClient };
Example code for
hotel.js
import { neon } from '@neondatabase/serverless'; import dotenv from 'dotenv'; dotenv.config(); const sql = neon(process.env.DATABASE_URL); const getAllHotels = async () => { const rows = await sql`SELECT * FROM hotels`; return rows; }; export { getAllHotels };
Example code for
reservation.js
import { neon } from '@neondatabase/serverless'; import dotenv from 'dotenv'; dotenv.config(); const sql = neon(process.env.DATABASE_URL); const getAvailableReservations = async () => { const rows = await sql` SELECT * FROM reservations WHERE status = ${'available'} `; return rows; }; export { getAvailableReservations };
b. Modify the
functions/
folder by adding the function files:In the
functions/
folder, remove the default file, and then add three function management files (manageClients.js
,manageHotels.js
, andmanageReservations.js
).Example for
manageClients.js
// src/functions/manageClients.js const { app } = require('@azure/functions'); const { getAllClients, addClient } = require('../database/client'); app.http('manageClients', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: async (request, context) => { context.log(`HTTP function processed request for url "${request.url}"`); if (request.method === 'GET') { try { const clients = await getAllClients(); console.table(clients); return { body: clients, }; } catch (error) { context.log('Error fetching clients:', error); return { status: 500, body: 'Error retrieving clients.', }; } } if (request.method === 'POST') { try { const { first_name, last_name, email, phone_number } = await request.json(); if (!first_name || !last_name || !email || !phone_number) { return { status: 400, body: 'Missing required fields: first_name, last_name, email, phone_number.', }; } const newClient = await addClient(first_name, last_name, email, phone_number); console.table(newClient); return { status: 201, body: newClient, }; } catch (error) { context.log('Error adding client:', error); return { status: 500, body: 'Error adding client.', }; } } }, });
Example for
manageHotels.js
// src/functions/manageHotels.js const { app } = require('@azure/functions'); const { getAllHotels } = require('../database/hotel'); app.http('manageHotels', { methods: ['GET'], authLevel: 'anonymous', handler: async (request, context) => { context.log(`HTTP function processed request for url "${request.url}"`); try { const hotels = await getAllHotels(); return { body: hotels, }; } catch (error) { context.log('Error fetching hotels:', error); return { status: 500, body: 'Error retrieving hotels.', }; } }, });
Example for
manageReservations.js
// src/functions/manageReservations.js const { app } = require('@azure/functions'); const { getAvailableReservations } = require('../database/reservation'); app.http('manageReservations', { methods: ['GET'], authLevel: 'anonymous', handler: async (request, context) => { context.log(`HTTP function processed request for url "${request.url}"`); try { const reservations = await getAvailableReservations(); return { body: reservations, }; } catch (error) { context.log('Error fetching reservations:', error); return { status: 500, body: 'Error retrieving available reservations.', }; } }, });
Feel free to extend this structure to include features such as adding new clients, creating new reservations, or even updating and deleting data, each with its own file and its own logic.
Step 3: Test the Function Locally
-
Run the Function Locally:
-
Open the integrated terminal in VS Code.
-
Run the following command
npm run start
, which will executefunc start
to start the project and launch the functions:npm run start
-
-
Test with a Browser or Postman:
- Open a browser and navigate to
http://localhost:7071/api/manageClients
to test your function. - You can also use a tool like Postman to send HTTP requests.
- Open a browser and navigate to
Step 4: Test and Deploy the Function to Azure
-
Deploy Your Function:
- Open the command palette with
Ctrl+Shift+P
and typeAzure Functions: Deploy to Function App...
. - Follow the instructions to select your Azure subscription and choose or create a Function App, then complete the deployment process.
- Open the command palette with
-
Test the Function:
Use a tool like Postman to send an HTTP request to the Azure Function, for example:
https://your-azure-function-url?client_id=1234
This will return the information of the client with the ID 1234, if present in the database.
Conclusion
We have demonstrated how combining Neon and Azure Functions enables the development of fast, scalable applications while reducing the complexity associated with managing infrastructure. With this combination, you can efficiently query your Postgres database without worrying about server maintenance. Moreover, Neon simplifies the scalability of your applications, making it an ideal choice for many modern projects.
Additional Resources
- Neon Documentation - Comprehensive documentation for Neon's database services, including guides, tutorials, and API references.
- Azure Functions Documentation
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more details, see Getting Support.