{"id":1648,"date":"2022-12-13T15:05:23","date_gmt":"2022-12-13T23:05:23","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sql\/?p=1648"},"modified":"2022-12-13T15:05:23","modified_gmt":"2022-12-13T23:05:23","slug":"developing-with-azure-sql-bindings-and-azure-sql-trigger-for-azure-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sql\/developing-with-azure-sql-bindings-and-azure-sql-trigger-for-azure-functions\/","title":{"rendered":"Developing with Azure SQL bindings and Azure SQL trigger for Azure Functions"},"content":{"rendered":"<p>The Azure Functions SQL trigger will invoke an Azure Function when data is changed in a SQL database table, enabling new event-driven scenarios with SQL database. Accessing data in a SQL database with Azure Functions can be accomplished more quickly with the Azure SQL bindings for Azure Functions integration.\u00a0 Azure SQL bindings for Azure Functions are available in public preview for all languages supported by Azure Functions and the Azure SQL trigger for Azure Functions is available in public preview for C# Azure Functions.<\/p>\n<p><!--more--><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/sqlbindings-summary.png\"><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/sqlbindings-summary-1024x517.png\" alt=\"Image sqlbindings summary\" width=\"640\" height=\"323\" \/><\/a><\/p>\n<h2>Guestbook sample application<\/h2>\n<p>A website guestbook is a simple form and list interface where users to input their messages for other website visitors to read.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook.png\"><img decoding=\"async\" class=\"aligncenter size-large wp-image-1655\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-1024x547.png\" alt=\"Image guestbook\" width=\"640\" height=\"342\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-1024x547.png 1024w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-300x160.png 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-768x410.png 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-1536x820.png 1536w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook.png 1699w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>In this sample application (<a href=\"https:\/\/aka.ms\/passguestbook\">aka.ms\/passguestbook<\/a>), we have created the web interface with an Azure Static Web App and connected it with an Azure SQL Database through Azure SQL bindings for Azure Functions.\u00a0 By leveraging SQL bindings we&#8217;ve reduced the boilerplate code and time to develop for our backend APIs for both SQL read and write operations.<\/p>\n<p><figure id=\"attachment_1650\" aria-labelledby=\"figcaption_attachment_1650\" class=\"wp-caption aligncenter\" ><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture.png\"><img decoding=\"async\" class=\"wp-image-1650 size-large\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture-1024x577.png\" alt=\"Image guestbook architecture\" width=\"640\" height=\"361\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture-1024x577.png 1024w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture-300x169.png 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture-768x433.png 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2022\/12\/guestbook-architecture.png 1298w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"figcaption_attachment_1650\" class=\"wp-caption-text\">Guestbook sample application architecture<\/figcaption><\/figure><\/p>\n<p>&nbsp;<\/p>\n<p>For example, writing a POST request body to the database (#1 in the figure above) is accomplished with the following JavaScript Azure Function.\u00a0 No driver installation or insert statement creation required as a part of the Azure Function to <a href=\"https:\/\/github.com\/Azure-Samples\/sql-bindings-guestbook\/tree\/main\/apis\/js-api\/AddEntry\">add an entry to the database<\/a>.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">module.exports = async function (context, req) {\r\n    context.log(req.body);\r\n\r\n    if (req.body) {\r\n        \/\/ create timestamp\r\n        var date = new Date();\r\n        \/\/ setup new entry\r\n        newRow = {\r\n            TextEntry: req.body.newEntry,\r\n            DisableView: false,\r\n            DateCreated: date.toISOString(),\r\n            DateModerated: null\r\n        };\r\n        context.bindings.newEntry = newRow;\r\n        context.res = {\r\n            body: newRow,\r\n            mimetype: \"application\/json\",\r\n            status: 201\r\n        };\r\n    } else {\r\n        context.res = {\r\n            status: 400,\r\n            body: \"Please pass a valid entry in the request body\"\r\n        };\r\n    }\r\n}<\/code><\/pre>\n<p>&nbsp;<\/p>\n<h3>Azure Functions SQL trigger<\/h3>\n<p>The Azure Functions SQL trigger is leveraged to perform moderation on guestbook entries after they&#8217;re inserted to the database.\u00a0 With this application pattern we&#8217;re able to perform subsequent actions, including data enrichment, without delaying interaction with the user interface.\u00a0 Additionally, we can ensure that any data inserted into the guestbook entry table is moderated if there was an additional application writing to the table.<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">[FunctionName(\"Moderator\")]\r\npublic static void Run(\r\n    [SqlTrigger(\"[app].[Entry]\", ConnectionStringSetting = \"SqlConnectionString\")] IReadOnlyList&lt;SqlChange&lt;Entry&gt;&gt; changes,\r\n    [Sql(\"app.Entry\", ConnectionStringSetting = \"SqlConnectionString\")] out Entry[] entryUpdates,\r\n    [Sql(\"app.Moderation\", ConnectionStringSetting = \"SqlConnectionString\")] out Moderation[] moderationUpdates,\r\n    ILogger log)\r\n{\r\n    List&lt;Entry&gt; updates = new List&lt;Entry&gt;();\r\n    List&lt;Moderation&gt; moderation = new List&lt;Moderation&gt;();\r\n    \/\/ content moderation setup\r\n    string SubscriptionKey = Environment.GetEnvironmentVariable(\"ModeratorKey\");\r\n    string ContentEndpoint = Environment.GetEnvironmentVariable(\"ModeratorEndpoint\");\r\n    ContentModeratorClient clientText = new ContentModeratorClient(new ApiKeyServiceClientCredentials(SubscriptionKey));\r\n    clientText.Endpoint = ContentEndpoint;\r\n\r\n    foreach (SqlChange&lt;Entry&gt; change in changes)\r\n    {\r\n        log.LogInformation($\"Change operation: {change.Operation}\");\r\n        if (change.Operation == SqlChangeOperation.Insert)\r\n        {\r\n            \/\/ send the new entry for moderation\r\n            string toModerate = change.Item.TextEntry;\r\n...<\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/Azure-Samples\/sql-bindings-guestbook\/blob\/main\/apis\/net-api\/Moderator.cs\">This function is triggered<\/a> by all changes to the table app.Entry, but only on change type Insert does it step into the moderation branch of execution.\u00a0 Later in the function it sets values for entryUpdates and moderationUpdates, which are used by SQL output bindings in the same Azure Function to update the entry in the SQL table.\u00a0 Execution takes less than half a second, including the time for an Azure content moderation service to evaluate the text in the post for inappropriate language.<\/p>\n<h3>Authentication<\/h3>\n<p>Connectivity between the Azure Functions and Azure SQL Database is specified with the connection string application setting on the Azure Functions.\u00a0 With system-assigned managed identity enabled on the Azure Functions, passwordless connection strings can be used to authenticate the Azure Function to Azure SQL Database:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">Server=demo.database.windows.net; Authentication=Active Directory Managed Identity; Database=guestbook<\/code><\/pre>\n<p>A walk-through document is available with information on creating up the managed identity users in Azure SQL Database: <a href=\"https:\/\/learn.microsoft.com\/azure\/azure-functions\/functions-identity-access-azure-sql-with-managed-identity\">https:\/\/learn.microsoft.com\/azure\/azure-functions\/functions-identity-access-azure-sql-with-managed-identity<\/a><\/p>\n<h3>Configuring the database for use with Azure Functions SQL trigger<\/h3>\n<p>Utilizing change tracking, the Azure Function SQL trigger will poll the database for updates and invoke the function with net changes from the latest period.\u00a0 The polling duration and maximum number of updates to be processed at a time can be <a href=\"https:\/\/learn.microsoft.com\/azure\/azure-functions\/functions-bindings-azure-sql-trigger#configuration\">set to custom values in application settings<\/a> to allow for different application scale based on specific use cases and resource sizing.<\/p>\n<p>Enabling change tracking on the database occurs at both the database level and on specific table, both of which are required for the SQL trigger.<\/p>\n<pre class=\"prettyprint language-sql\"><code class=\"language-sql\">ALTER DATABASE [guestbook]\r\nSET CHANGE_TRACKING = ON;\r\nGO\r\n\r\nALTER TABLE [app].[Entry] ENABLE CHANGE_TRACKING;\r\nGO<\/code><\/pre>\n<p>The Azure Function with the SQL trigger completes the final setup step itself at startup, creating an <em>az_func<\/em> schema if needed and tables for tracking the processing of changes.<\/p>\n<h2>Next steps<\/h2>\n<ul>\n<li>Dive in with the application sample code at <a href=\"https:\/\/aka.ms\/passguestbook\">aka.ms\/passguestbook<\/a>\u00a0and catch a summary of the application on <a href=\"https:\/\/www.youtube.com\/watch?v=01nhp4OAk0c\">Data Exposed<\/a>.<\/li>\n<li>Learn more about the Azure SQL trigger for Azure Functions in the official documentation (<a href=\"https:\/\/aka.ms\/sqltrigger\">aka.ms\/sqltrigger<\/a>) and see additional samples in the <a href=\"https:\/\/github.com\/Azure\/azure-functions-sql-extension\">Azure SQL bindings for Azure Functions<\/a> repository.<\/li>\n<li>For an introduction to utilizing Azure SQL bindings for Azure Functions with Azure Static Web Apps, check out this <a href=\"https:\/\/www.youtube.com\/watch?v=Iip8zWGn7Ew\">video from \/\/Build 2022<\/a>.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The Azure Functions SQL trigger will invoke an Azure Function when data is changed in a SQL database table, enabling new event-driven scenarios with SQL database. Accessing data in a SQL database with Azure Functions can be accomplished more quickly with the Azure SQL bindings for Azure Functions integration.\u00a0 Azure SQL bindings for Azure Functions [&hellip;]<\/p>\n","protected":false},"author":46830,"featured_media":1649,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[444,1,430],"tags":[541,435],"class_list":["post-1648","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-azure-sql","category-javascript","tag-azure-functions","tag-functions"],"acf":[],"blog_post_summary":"<p>The Azure Functions SQL trigger will invoke an Azure Function when data is changed in a SQL database table, enabling new event-driven scenarios with SQL database. Accessing data in a SQL database with Azure Functions can be accomplished more quickly with the Azure SQL bindings for Azure Functions integration.\u00a0 Azure SQL bindings for Azure Functions [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/1648","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/users\/46830"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/comments?post=1648"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/1648\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media\/1649"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media?parent=1648"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/categories?post=1648"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/tags?post=1648"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}