49

Calling an API from ASP.NET Web Form is very easy.

WebClient wc = new WebClient();
string urlData = wc.DownloadString("http://xxx.xxx.xx.xx/sssss/getResponse.do?ID=testing");

But can we call an API from SQL Server stored procedure.

If yes then how can we call an API from a SQL Server stored procedure and how can we get API response?

Dale K
  • 25,246
  • 15
  • 42
  • 71
Ashish Rathore
  • 2,546
  • 9
  • 55
  • 91
  • 7
    You can do that inside of [CLR Stored Proc](http://msdn.microsoft.com/en-us/library/ms131094(SQL.100).aspx) –  Feb 27 '14 at 12:44

9 Answers9

54

Please see a link for more details.

Declare @Object as Int;
Declare @ResponseText as Varchar(8000);

Code Snippet
Exec sp_OACreate 'MSXML2.XMLHTTP', @Object OUT;
Exec sp_OAMethod @Object, 'open', NULL, 'get',
                 'http://www.webservicex.com/stockquote.asmx/GetQuote?symbol=MSFT', --Your Web Service Url (invoked)
                 'false'
Exec sp_OAMethod @Object, 'send'
Exec sp_OAMethod @Object, 'responseText', @ResponseText OUTPUT

Select @ResponseText

Exec sp_OADestroy @Object
fastobject
  • 1,372
  • 12
  • 8
  • 1
    Hi, this code is not working when trying to call an ASP.NET Web API endpoint. It accepts JSON. How can I change this code to make it work? – Bat_Programmer Dec 06 '16 at 02:32
  • @Bat_Programmer, what issue are you facing? – Francesco Mantovani Oct 01 '18 at 08:21
  • e.g I want to call an API that needs for `USERID` and `API KEY`( as password) – Muhammad Waheed Mar 06 '20 at 07:02
  • 1
    An auth data can be passed in different way like through HTTP request header(s) of body. It depends from HTTP Service that you are going to call/use. to set HTTP (Authorization) header you can try to add following code before 'send' method: `Exec sp_OAMethod @Object, 'setRequestHeader', 'Authorization', 'header value like token'`. To set body you can pass content into 'send' method like following: `Exec sp_OAMethod @Object, 'send' , 'body content'` – fastobject Mar 08 '20 at 21:57
41

I worked so much, I hope my effort might help you out.

Just paste this into your SSMS and press F5:

Declare @Object as Int;
DECLARE @hr  int
Declare @json as table(Json_Table nvarchar(max))

Exec @hr=sp_OACreate 'MSXML2.ServerXMLHTTP.6.0', @Object OUT;
IF @hr <> 0 EXEC sp_OAGetErrorInfo @Object
Exec @hr=sp_OAMethod @Object, 'open', NULL, 'get',
                 'http://overpass-api.de/api/interpreter?data=[out:json];area[name=%22Auckland%22]-%3E.a;(node(area.a)[amenity=cinema];way(area.a)[amenity=cinema];rel(area.a)[amenity=cinema];);out;', --Your Web Service Url (invoked)
                 'false'
IF @hr <> 0 EXEC sp_OAGetErrorInfo @Object
Exec @hr=sp_OAMethod @Object, 'send'
IF @hr <> 0 EXEC sp_OAGetErrorInfo @Object
Exec @hr=sp_OAMethod @Object, 'responseText', @json OUTPUT
IF @hr <> 0 EXEC sp_OAGetErrorInfo @Object

INSERT into @json (Json_Table) exec sp_OAGetProperty @Object, 'responseText'
-- select the JSON string
select * from @json
-- Parse the JSON string
SELECT * FROM OPENJSON((select * from @json), N'$.elements')
WITH (   
      [type] nvarchar(max) N'$.type'   ,
      [id]   nvarchar(max) N'$.id',
      [lat]   nvarchar(max) N'$.lat',
      [lon]   nvarchar(max) N'$.lon',
      [amenity]   nvarchar(max) N'$.tags.amenity',
      [name]   nvarchar(max) N'$.tags.name'     
)
EXEC sp_OADestroy @Object

This query will give you 3 results:

1. Catch the error in case something goes wrong (don't panic, it will always show you an error above 4000 characters because NVARCHAR(MAX) can only store till 4000 characters)

2. Put the JSON into a string (which is what we want)

3. BONUS: parse the JSON and nicely store the data into a table (how cool is that?)

enter image description here

Francesco Mantovani
  • 10,216
  • 13
  • 73
  • 113
  • can kerb auth be added to this? – JoeRod Aug 29 '19 at 17:30
  • @JoeRod, do you want to download data from Kerberos API? – Francesco Mantovani Aug 30 '19 at 07:26
  • @FrancescoMantovani I want to authenticate to the API endpoint with kerberos – JoeRod Sep 04 '19 at 02:34
  • I don't get it, maybe you are searching for this? https://www.sqlservercentral.com/articles/configuring-kerberos-authentication – Francesco Mantovani Sep 05 '19 at 08:02
  • 2
    Thanks for the bonus JSON parsing – Dave Pile Feb 25 '20 at 22:26
  • Could you please elaborate on result 1 (always having Error 0x80042725)? I don't understand how the link provided is related. – Luis Fernando Cantu Apr 14 '21 at 18:41
  • @LuisFernandoCantu, Try to retrieve a JSON below 4000, you will see that the error goes away. In the example I'm retrieving all cinemas in Auckland, try to retrieve them from a small town. – Francesco Mantovani Apr 15 '21 at 06:15
  • I'm dealing with the issue of having only a limit of 100 records showing up per call. How can I write a loop to grab all records? the OPENJSON fails because my current loop creates three rows in my JSON table. – Anthony J Apr 05 '23 at 19:03
  • @AnthonyJ, open a new question on StackOverflow, mention me in the comment and I will try out to run your code. Is the API a public API that I can reach or something internal? 100 is a peculiar number, are you sure there is no pagination in the code? – Francesco Mantovani Apr 06 '23 at 06:59
  • 1
    @FrancescoMantovani I figured out my issue. The API had a limit of 100 so I just created a while loop in SQL and changed the Offset until all records were returned! thanks again your example was exactly what I needed. Another good thing to note, is that if your OPENJSON table has more than one row you have to do a CROSS APPLY or it will fail. – Anthony J Apr 10 '23 at 18:37
  • Thank you @AnthonyJ, I will write a blog post as soon as I have time as there is a new feature in Azure SQL Database that allows to query REST API – Francesco Mantovani Apr 19 '23 at 05:48
16

Screams in to the void - just "no" don't do it. This is a dumb idea.

Integrating with external data sources is what SSIS is for, or write a dot net application/service which queries the box and makes the API calls.

Writing CLR code to enable a SQL process to call web-services is the sort of thing that can bring a SQL box to its knees if done badly - imagine putting the the CLR function in a view somewhere - later someone else comes along not knowing what you've donem and joins on that view with a million row table - suddenly your SQL box is making a million individual webapi calls.

The whole idea is insane.

This doing sort of thing is the reason that enterprise DBAs dont' trust developers.

CLR is the kind of great power, which brings great responsibility, and the above is an abuse of it.

Barry Wimlett
  • 353
  • 2
  • 6
  • 2
    I also think that it does not separate the code base well, and also make you still need to set the code in two locations. Maybe I am old school and wrong, but I think it should be separated out into the C# web service calling app, unless this is done on a minimal levle. – Casey ScriptFu Pharr Mar 19 '20 at 13:57
  • "[Doing] this sort of thing is the reason that enterprise DBAs don't trust developers" - As both a DBA and a Lead Developer I agree with this statement! – melodiouscode Jan 08 '21 at 11:35
  • I would like to pitch my use case. We are working with ADF and trying to check an API response. – SouravA Jan 23 '23 at 05:41
6

I think it would be easier using this CLR Stored procedure SQL-APIConsumer:

 exec [dbo].[APICaller_POST]
     @URL = 'http://localhost:5000/api/auth/login'
    ,@BodyJson = '{"Username":"gdiaz","Password":"password"}'

It has multiple procedures that allows you calling API that required a parameters and even passing multiples headers and tokens authentications.

enter image description here

enter image description here

Geraldo Diaz
  • 346
  • 5
  • 7
2

Disclaimer: I work for ZappySys (Company which makes API Drivers for Apps like SQL Server)

Calling API in SQL Server Stored Proc can be done following 3 ways (at least based on what I know). Each method has its own PROS and CONS so choose it as per your need.

METHOD-1 (use sp_OAxxx)

Use sp_OAMethod (as described by Francesco Mantovani)

PROS

  • No extra Programming needed like SQL CLR approach suggested in Method-2

CONS

  • Calling sp_OAxxx procs considered a highly unsecure method and not recommended as per this SO Post.
  • By default, only admin group can execute this Procs

METHOD-2 (code custom CLR Proc)

Use CLR Stored Proc (as described by Geraldo Diaz)

PROS

  • You can write highly custom logic to call API using any .net Language (like C# / VB.net)

CONS

  • Requires Programming Knowledge
  • Requires additional steps to register Custom CLR dll and configure CLR security

METHOD-3 (Using 3rd-party API Driver)

You can use 3rd party API Driver and call REST API in SQL Server like this way (Linked Server + OPENQUERY) - See example API data load in stored proc in below image.

PROS

  • No need to learn custom programming to code your own dll
  • No need to unblock / configure additional security on SQL Server (unlike Method-1 and 2)

CONS

  • Not FREE solution (Requires purchase of Driver)

Calling REST API in SQL Server stored proc

ZappySys
  • 91
  • 3
1

Simple SQL triggered API call without building a code project

I know this is far from perfect or architectural purity, but I had a customer with a short term, critical need to integrate with a third party product via an immature API (no wsdl) I basically needed to call the API when a database event occurred. I was given basic call info - URL, method, data elements and Token, but no wsdl or other start to import into a code project. All recommendations and solutions seemed start with that import.

I used the ARC (Advanced Rest Client) Chrome extension and JaSON to test the interaction with the Service from a browser and refine the call. That gave me the tested, raw call structure and response and let me play with the API quickly. From there, I started trying to generate the wsdl or xsd from the json using online conversions but decided that was going to take too long to get working, so I found cURL (clouds part, music plays). cURL allowed me to send the API calls to a local manager from anywhere. I then broke a few more design rules and built a trigger that queued the DB events and a SQL stored procedure and scheduled task to pass the parameters to cURL and make the calls. I initially had the trigger calling XP_CMDShell (I know, booo) but didn't like the transactional implications or security issues, so switched to the Stored Procedure method.

In the end, DB insert matching the API call case triggers write to Queue table with parameters for API call Stored procedure run every 5 seconds runs Cursor to pull each Queue table entry, send the XP_CMDShell call to the bat file with parameters Bat file contains Curl call with parameters inserted sending output to logs. Works well.

Again, not perfect, but for a tight timeline, and a system used short term, and that can be closely monitored to react to connectivity and unforeseen issues, it worked.

Hope that helps someone struggling with limited API info get a solution going quickly.

Todd C
  • 29
  • 1
1

I'd recommend using a CLR user defined function, if you already know how to program in C#, then the code would be;

using System.Data.SqlTypes;
using System.Net;

public partial class UserDefinedFunctions
{
 [Microsoft.SqlServer.Server.SqlFunction]
 public static SqlString http(SqlString url)
 {
  var wc = new WebClient();
  var html = wc.DownloadString(url.Value);
  return new SqlString (html);
 }
}

And here's installation instructions; https://blog.dotnetframework.org/2019/09/17/make-a-http-request-from-sqlserver-using-a-clr-udf/

Fiach Reid
  • 6,149
  • 2
  • 30
  • 34
1

Today there is an easier way to call a REST endpoint from Azure SQL database (support for SQL Server and Azure SQL MI will come in future), thanks to the newly introduced system stored procedure sp_invoke_external_rest_endpoint:

https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql

for example:

DECLARE @ret INT, @response NVARCHAR(MAX);

EXEC @ret = sp_invoke_external_rest_endpoint
  @url = N'https://<APP_NAME>.azurewebsites.net/api/<FUNCTION_NAME>?key1=value1',
  @headers = N'{"header1":"value_a", "header2":"value2", "header1":"value_b"}',
  @payload = N'{"some":{"data":"here"}}',
  @response = @response OUTPUT;

SELECT @ret AS ReturnCode, @response AS Response;
mauridb
  • 1,467
  • 9
  • 12
  • I followed your official Microsoft documentation, no matter the API (https://apipheny.io/free-api/) I always receive the error `Connections to the domain api.website.org are not allowed.`. – Francesco Mantovani Apr 24 '23 at 21:31
  • The documentation specifies that only a set of allow-listed domains can be called (for security reasons). – mauridb May 04 '23 at 16:07
0

The SQL Query select * from openjson ... works only with SQL version 2016 and higher. Need the SQL compatibility mode 130.

derloopkat
  • 6,232
  • 16
  • 38
  • 45
chris
  • 1
  • 2