Enterprise API Integration Guide: SQL Server 2025 + JWT + Complex JSON Handling

 




Enterprise API Integration Guide: SQL Server 2025 + JWT + Complex JSON Handling

1. The New Era of Database Connectivity

Welcome to FreeLearning365.com, where we explore cutting-edge technology solutions! In today's data-driven world, databases are no longer isolated silos but dynamic components of interconnected systems. SQL Server 2025 introduces groundbreaking capabilities that transform how we integrate with external APIs, handle complex data structures, and secure enterprise data flows.

The Challenge: Modern applications demand real-time data integration with various REST APIs, often requiring JWT authentication, handling complex nested JSON responses, processing bulk data efficiently, and maintaining enterprise-grade security—all while ensuring performance and reliability.

The Solution: SQL Server 2025's enhanced REST API capabilities, combined with strategic implementation patterns, provide a comprehensive framework for building robust, secure, and scalable API integrations directly from your database layer.


2. What You'll Learn

By the end of this comprehensive guide, you'll master:

✅ JWT Authentication Management - Secure token acquisition, refresh, and storage with encryption
✅ Complex JSON Processing - Handling nested JSON structures with SQL Server's native JSON functions
✅ Bulk Data Operations - Efficiently processing thousands of API records with pagination and chunking
✅ Enterprise Security - Implementing encryption, rate limiting, and comprehensive audit logging
✅ Error Handling & Resilience - Building fault-tolerant systems with retry logic and monitoring
✅ Real API Integration - Working with free APIs like JSONPlaceholder and ReqRes.in
✅ Performance Optimization - Best practices for high-volume API integrations
✅ Monitoring & Maintenance - Creating dashboards and automated cleanup procedures


Useful Resources:



1. Free Open-Source API Endpoints for Testing

A. Available Free APIs with JWT Support:

  1. JSONPlaceholder - Fake REST API (No auth required)

  2. ReqRes.in - Supports JWT authentication

  3. GoRest API - Free tier with JWT

  4. JSON Server - Local mock API with JWT

2. Complete Setup with Real Examples

A. Database Schema with Enhanced Security

sql
-- Enable Always Encrypted for sensitive data
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'YourStrongPassword123!';

CREATE CERTIFICATE ApiEncryptionCert
WITH SUBJECT = 'API Secrets Encryption';

CREATE SYMMETRIC KEY ApiSymmetricKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE ApiEncryptionCert;

-- Main configuration table with encryption
CREATE TABLE dbo.ApiConfig (
    ConfigId INT IDENTITY(1,1) PRIMARY KEY,
    ApiName NVARCHAR(100) NOT NULL UNIQUE,
    BaseUrl NVARCHAR(500) NOT NULL,
    AuthEndpoint NVARCHAR(500),
    ClientId VARBINARY(MAX), -- Encrypted
    ClientSecret VARBINARY(MAX), -- Encrypted
    TokenEndpoint NVARCHAR(500),
    GrantType NVARCHAR(50) DEFAULT 'password',
    Scope NVARCHAR(500),
    TokenExpiryMinutes INT DEFAULT 55,
    MaxRetries INT DEFAULT 3,
    TimeoutSeconds INT DEFAULT 30,
    RateLimitPerMinute INT DEFAULT 60,
    IsActive BIT DEFAULT 1,
    CreatedBy SYSNAME DEFAULT SUSER_SNAME(),
    CreatedDate DATETIME DEFAULT GETDATE(),
    UpdatedBy SYSNAME,
    UpdatedDate DATETIME,
    INDEX IX_ApiName (ApiName),
    INDEX IX_IsActive (IsActive)
);

-- Encrypted token storage
CREATE TABLE dbo.ApiTokenStore (
    TokenId INT IDENTITY(1,1) PRIMARY KEY,
    ConfigId INT FOREIGN KEY REFERENCES dbo.ApiConfig(ConfigId),
    AccessToken VARBINARY(MAX) NOT NULL, -- Encrypted
    TokenType NVARCHAR(50),
    ExpiresIn INT,
    RefreshToken VARBINARY(MAX), -- Encrypted
    Scope NVARCHAR(500),
    AcquiredAt DATETIME DEFAULT GETDATE(),
    ExpiresAt DATETIME,
    IsValid BIT DEFAULT 1,
    INDEX IX_ConfigId_Valid (ConfigId, IsValid),
    INDEX IX_ExpiresAt (ExpiresAt)
);

-- Comprehensive error logging
CREATE TABLE dbo.ApiErrorLog (
    ErrorId BIGINT IDENTITY(1,1) PRIMARY KEY,
    ConfigId INT,
    ApiName NVARCHAR(100),
    Endpoint NVARCHAR(500),
    HttpMethod NVARCHAR(10),
    ErrorCode NVARCHAR(50),
    ErrorMessage NVARCHAR(MAX),
    StatusCode INT,
    ResponseBody NVARCHAR(MAX),
    RequestPayload NVARCHAR(MAX),
    RequestHeaders NVARCHAR(MAX),
    ResponseHeaders NVARCHAR(MAX),
    StackTrace NVARCHAR(MAX),
    RetryCount INT,
    BatchId UNIQUEIDENTIFIER,
    CorrelationId UNIQUEIDENTIFIER DEFAULT NEWID(),
    ErrorTime DATETIME DEFAULT GETDATE(),
    Resolved BIT DEFAULT 0,
    ResolvedBy SYSNAME,
    ResolvedDate DATETIME,
    INDEX IX_ErrorTime (ErrorTime),
    INDEX IX_Resolved (Resolved),
    INDEX IX_CorrelationId (CorrelationId),
    INDEX IX_BatchId_Error (BatchId)
);

-- Audit log for all API calls
CREATE TABLE dbo.ApiAuditLog (
    AuditId BIGINT IDENTITY(1,1) PRIMARY KEY,
    ConfigId INT,
    ApiName NVARCHAR(100),
    Endpoint NVARCHAR(500),
    HttpMethod NVARCHAR(10),
    StatusCode INT,
    RequestSizeBytes INT,
    ResponseSizeBytes INT,
    DurationMs INT,
    CorrelationId UNIQUEIDENTIFIER DEFAULT NEWID(),
    CalledBy SYSNAME DEFAULT SUSER_SNAME(),
    CalledAt DATETIME DEFAULT GETDATE(),
    Success BIT,
    INDEX IX_CalledAt (CalledAt),
    INDEX IX_CorrelationId_Audit (CorrelationId),
    INDEX IX_ApiName_Method (ApiName, HttpMethod)
);

-- Rate limiting control
CREATE TABLE dbo.ApiRateLimit (
    RateLimitId INT IDENTITY(1,1) PRIMARY KEY,
    ConfigId INT FOREIGN KEY REFERENCES dbo.ApiConfig(ConfigId),
    EndpointPattern NVARCHAR(500),
    MaxRequestsPerMinute INT DEFAULT 60,
    CurrentRequestCount INT DEFAULT 0,
    LastResetTime DATETIME DEFAULT GETDATE(),
    IsActive BIT DEFAULT 1
);

-- Response staging with compression
CREATE TABLE dbo.ApiResponseStage (
    StageId BIGINT IDENTITY(1,1) PRIMARY KEY,
    BatchId UNIQUEIDENTIFIER DEFAULT NEWID(),
    ConfigId INT,
    Endpoint NVARCHAR(500),
    ResponseJson NVARCHAR(MAX),
    ResponseHeaders NVARCHAR(MAX),
    StatusCode INT,
    IsCompressed BIT DEFAULT 0,
    IsProcessed BIT DEFAULT 0,
    ProcessedDate DATETIME,
    CreatedDate DATETIME DEFAULT GETDATE(),
    INDEX IX_BatchId (BatchId),
    INDEX IX_IsProcessed (IsProcessed),
    INDEX IX_CreatedDate (CreatedDate)
) WITH (DATA_COMPRESSION = PAGE);

-- Example target table for complex nested JSON
CREATE TABLE dbo.UsersData (
    UserId INT PRIMARY KEY,
    Email NVARCHAR(200),
    FirstName NVARCHAR(100),
    LastName NVARCHAR(100),
    Avatar NVARCHAR(500),
    CompanyName NVARCHAR(200),
    CompanyCatchPhrase NVARCHAR(500),
    CompanyBs NVARCHAR(500),
    AddressStreet NVARCHAR(200),
    AddressSuite NVARCHAR(100),
    AddressCity NVARCHAR(100),
    AddressZipCode NVARCHAR(20),
    AddressGeoLat DECIMAL(10,6),
    AddressGeoLng DECIMAL(10,6),
    Phone NVARCHAR(50),
    Website NVARCHAR(200),
    Posts NVARCHAR(MAX), -- Nested JSON array
    Todos NVARCHAR(MAX), -- Nested JSON array
    Albums NVARCHAR(MAX), -- Nested JSON array
    RawResponse NVARCHAR(MAX),
    LastSynced DATETIME DEFAULT GETDATE(),
    CreatedDate DATETIME DEFAULT GETDATE(),
    INDEX IX_Email (Email),
    INDEX IX_LastName (LastName)
);

3. Real API Examples Setup

A. Insert Real Free API Configurations

sql
-- Open symmetric key for encryption
OPEN SYMMETRIC KEY ApiSymmetricKey
DECRYPTION BY CERTIFICATE ApiEncryptionCert;

-- 1. ReqRes.in API (Supports JWT)
INSERT INTO dbo.ApiConfig (
    ApiName, BaseUrl, AuthEndpoint,
    ClientId, ClientSecret, TokenEndpoint,
    GrantType, Scope, TokenExpiryMinutes,
    MaxRetries, TimeoutSeconds, RateLimitPerMinute
) VALUES (
    'ReqResAPI',
    'https://reqres.in/api/',
    '/login',
    ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), 'eve.holt@reqres.in'),
    ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), 'cityslicka'),
    '/login',
    'password',
    'read write',
    30, 3, 30, 10 -- Limited to 10 req/min (free API)
);

-- 2. JSONPlaceholder API (No auth - for testing)
INSERT INTO dbo.ApiConfig (
    ApiName, BaseUrl, TokenExpiryMinutes,
    MaxRetries, TimeoutSeconds, RateLimitPerMinute
) VALUES (
    'JSONPlaceholder',
    'https://jsonplaceholder.typicode.com/',
    NULL, 3, 30, 100
);

-- 3. GoRest API (Free tier with JWT)
INSERT INTO dbo.ApiConfig (
    ApiName, BaseUrl, AuthEndpoint,
    ClientId, ClientSecret, TokenEndpoint,
    GrantType, Scope, TokenExpiryMinutes
) VALUES (
    'GoRestAPI',
    'https://gorest.co.in/public/v2/',
    '/oauth/token',
    ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), 'your_client_id'),
    ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), 'your_client_secret'),
    '/oauth/token',
    'client_credentials',
    'public',
    3600
);

CLOSE SYMMETRIC KEY ApiSymmetricKey;

4. Enhanced JWT Token Management with Security

sql
CREATE OR ALTER PROCEDURE dbo.GetSecureApiToken
    @ConfigId INT,
    @ForceRefresh BIT = 0,
    @AccessToken NVARCHAR(MAX) OUTPUT,
    @TokenType NVARCHAR(50) OUTPUT,
    @CorrelationId UNIQUEIDENTIFIER OUTPUT,
    @ErrorMessage NVARCHAR(MAX) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET @CorrelationId = NEWID();
    
    DECLARE @StartTime DATETIME = GETDATE();
    DECLARE @RetryCount INT = 0;
    DECLARE @MaxRetries INT;
    DECLARE @Success BIT = 0;
    DECLARE @TokenResponse NVARCHAR(MAX);
    DECLARE @StatusCode INT;
    DECLARE @ResponseHeaders NVARCHAR(MAX);
    DECLARE @ApiName NVARCHAR(100);
    DECLARE @BaseUrl NVARCHAR(500);
    DECLARE @ClientId NVARCHAR(500);
    DECLARE @ClientSecret NVARCHAR(MAX);
    DECLARE @TokenEndpoint NVARCHAR(500);
    DECLARE @GrantType NVARCHAR(50);
    DECLARE @Scope NVARCHAR(500);
    
    BEGIN TRY
        -- Get API configuration
        SELECT 
            @ApiName = ApiName,
            @BaseUrl = BaseUrl,
            @TokenEndpoint = TokenEndpoint,
            @GrantType = GrantType,
            @Scope = Scope,
            @MaxRetries = MaxRetries
        FROM dbo.ApiConfig
        WHERE ConfigId = @ConfigId AND IsActive = 1;
        
        IF @ApiName IS NULL
        BEGIN
            SET @ErrorMessage = 'API configuration not found or inactive';
            THROW 50001, @ErrorMessage, 1;
        END
        
        -- Check rate limiting
        IF dbo.CheckRateLimit(@ConfigId, @TokenEndpoint) = 0
        BEGIN
            SET @ErrorMessage = 'Rate limit exceeded for token endpoint';
            THROW 50002, @ErrorMessage, 1;
        END
        
        -- Decrypt credentials
        OPEN SYMMETRIC KEY ApiSymmetricKey
        DECRYPTION BY CERTIFICATE ApiEncryptionCert;
        
        SELECT 
            @ClientId = CONVERT(NVARCHAR(500), DECRYPTBYKEY(ClientId)),
            @ClientSecret = CONVERT(NVARCHAR(MAX), DECRYPTBYKEY(ClientSecret))
        FROM dbo.ApiConfig
        WHERE ConfigId = @ConfigId;
        
        CLOSE SYMMETRIC KEY ApiSymmetricKey;
        
        -- Check for valid existing token
        IF @ForceRefresh = 0
        BEGIN
            OPEN SYMMETRIC KEY ApiSymmetricKey
            DECRYPTION BY CERTIFICATE ApiEncryptionCert;
            
            SELECT TOP 1 
                @AccessToken = CONVERT(NVARCHAR(MAX), DECRYPTBYKEY(AccessToken)),
                @TokenType = TokenType
            FROM dbo.ApiTokenStore
            WHERE ConfigId = @ConfigId 
                AND IsValid = 1 
                AND ExpiresAt > DATEADD(MINUTE, 2, GETDATE()) -- 2 minute buffer
            ORDER BY AcquiredAt DESC;
            
            CLOSE SYMMETRIC KEY ApiSymmetricKey;
            
            IF @AccessToken IS NOT NULL
            BEGIN
                SET @Success = 1;
                
                -- Log successful token retrieval
                INSERT INTO dbo.ApiAuditLog (
                    ConfigId, ApiName, Endpoint,
                    HttpMethod, StatusCode, DurationMs,
                    CorrelationId, Success
                ) VALUES (
                    @ConfigId, @ApiName, 'TokenCache',
                    'GET', 200, DATEDIFF(MILLISECOND, @StartTime, GETDATE()),
                    @CorrelationId, 1
                );
                
                RETURN;
            END
        END
        
        -- Prepare token request
        DECLARE @TokenUrl NVARCHAR(1000) = @BaseUrl + @TokenEndpoint;
        DECLARE @Payload NVARCHAR(MAX);
        DECLARE @Headers NVARCHAR(MAX);
        
        -- Different payloads based on grant type
        IF @GrantType = 'password' AND @ApiName = 'ReqResAPI'
        BEGIN
            SET @Payload = JSON_OBJECT(
                'email': @ClientId,
                'password': @ClientSecret
            );
            SET @Headers = '{"Content-Type":"application/json"}';
        END
        ELSE IF @GrantType = 'client_credentials'
        BEGIN
            SET @Payload = 'grant_type=client_credentials&client_id=' + 
                          @ClientId + '&client_secret=' + @ClientSecret;
            SET @Headers = '{"Content-Type":"application/x-www-form-urlencoded"}';
        END
        ELSE
        BEGIN
            SET @ErrorMessage = 'Unsupported grant type: ' + ISNULL(@GrantType, 'NULL');
            THROW 50003, @ErrorMessage, 1;
        END
        
        -- Retry loop with exponential backoff
        WHILE @RetryCount < @MaxRetries AND @Success = 0
        BEGIN
            BEGIN TRY
                -- Update rate limit
                EXEC dbo.UpdateRateLimit @ConfigId, @TokenEndpoint;
                
                -- Make API call with timeout
                EXEC sp_invoke_external_rest_endpoint
                    @url = @TokenUrl,
                    @method = 'POST',
                    @headers = @Headers,
                    @payload = @Payload,
                    @timeout = 30,
                    @response = @TokenResponse OUTPUT,
                    @response_headers = @ResponseHeaders OUTPUT,
                    @response_code = @StatusCode OUTPUT;
                
                -- Log the attempt
                INSERT INTO dbo.ApiAuditLog (
                    ConfigId, ApiName, Endpoint,
                    HttpMethod, StatusCode, DurationMs,
                    CorrelationId, Success
                ) VALUES (
                    @ConfigId, @ApiName, @TokenEndpoint,
                    'POST', @StatusCode, DATEDIFF(MILLISECOND, @StartTime, GETDATE()),
                    @CorrelationId, CASE WHEN @StatusCode = 200 THEN 1 ELSE 0 END
                );
                
                IF @StatusCode = 200
                BEGIN
                    -- Parse and encrypt token
                    DECLARE @ExpiresIn INT = JSON_VALUE(@TokenResponse, '$.expires_in');
                    DECLARE @RefreshToken NVARCHAR(MAX) = JSON_VALUE(@TokenResponse, '$.refresh_token');
                    
                    SET @AccessToken = JSON_VALUE(@TokenResponse, '$.access_token');
                    SET @TokenType = JSON_VALUE(@TokenResponse, '$.token_type');
                    SET @Scope = JSON_VALUE(@TokenResponse, '$.scope');
                    
                    -- Encrypt and store token
                    OPEN SYMMETRIC KEY ApiSymmetricKey
                    DECRYPTION BY CERTIFICATE ApiEncryptionCert;
                    
                    -- Invalidate previous tokens
                    UPDATE dbo.ApiTokenStore 
                    SET IsValid = 0 
                    WHERE ConfigId = @ConfigId;
                    
                    -- Store new token
                    INSERT INTO dbo.ApiTokenStore (
                        ConfigId, AccessToken, TokenType,
                        ExpiresIn, RefreshToken, Scope,
                        ExpiresAt
                    ) VALUES (
                        @ConfigId, 
                        ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), @AccessToken),
                        @TokenType,
                        @ExpiresIn,
                        ENCRYPTBYKEY(KEY_GUID('ApiSymmetricKey'), ISNULL(@RefreshToken, '')),
                        @Scope,
                        DATEADD(SECOND, ISNULL(@ExpiresIn, 1800), GETDATE())
                    );
                    
                    CLOSE SYMMETRIC KEY ApiSymmetricKey;
                    
                    SET @Success = 1;
                END
                ELSE
                BEGIN
                    -- Log error
                    INSERT INTO dbo.ApiErrorLog (
                        ConfigId, ApiName, Endpoint,
                        HttpMethod, ErrorMessage, StatusCode,
                        ResponseBody, RequestHeaders, ResponseHeaders,
                        CorrelationId, RetryCount
                    ) VALUES (
                        @ConfigId, @ApiName, @TokenUrl,
                        'POST', 'Token acquisition failed', @StatusCode,
                        LEFT(@TokenResponse, 4000), @Headers, @ResponseHeaders,
                        @CorrelationId, @RetryCount
                    );
                    
                    SET @RetryCount = @RetryCount + 1;
                    
                    -- Exponential backoff
                    IF @RetryCount < @MaxRetries
                        WAITFOR DELAY '00:00:' + RIGHT('00' + CAST(POWER(2, @RetryCount) AS VARCHAR(2)), 2);
                END
            END TRY
            BEGIN CATCH
                SET @ErrorMessage = ERROR_MESSAGE();
                
                INSERT INTO dbo.ApiErrorLog (
                    ConfigId, ApiName, Endpoint,
                    HttpMethod, ErrorMessage, StackTrace,
                    CorrelationId, RetryCount
                ) VALUES (
                    @ConfigId, @ApiName, @TokenUrl,
                    'POST', @ErrorMessage, 
                    ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10)),
                    @CorrelationId, @RetryCount
                );
                
                SET @RetryCount = @RetryCount + 1;
                
                IF @RetryCount < @MaxRetries
                    WAITFOR DELAY '00:00:' + RIGHT('00' + CAST(POWER(2, @RetryCount) AS VARCHAR(2)), 2);
            END CATCH
        END
        
        IF @Success = 0
        BEGIN
            SET @ErrorMessage = 'Failed to acquire token after ' + CAST(@MaxRetries AS NVARCHAR) + ' attempts';
            THROW 50004, @ErrorMessage, 1;
        END
        
    END TRY
    BEGIN CATCH
        SET @ErrorMessage = ERROR_MESSAGE();
        
        INSERT INTO dbo.ApiErrorLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, ErrorMessage, StackTrace,
            CorrelationId
        ) VALUES (
            @ConfigId, @ApiName, 'Token Acquisition',
            'POST', @ErrorMessage, 
            ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10)),
            @CorrelationId
        );
        
        THROW;
    END CATCH
END

5. Rate Limiting Functions

sql
CREATE OR ALTER FUNCTION dbo.CheckRateLimit
    (@ConfigId INT, @Endpoint NVARCHAR(500))
RETURNS BIT
AS
BEGIN
    DECLARE @Result BIT = 1;
    
    DECLARE @MaxRequests INT, @CurrentCount INT, @LastReset DATETIME;
    
    SELECT TOP 1
        @MaxRequests = ISNULL(rl.MaxRequestsPerMinute, c.RateLimitPerMinute),
        @CurrentCount = rl.CurrentRequestCount,
        @LastReset = rl.LastResetTime
    FROM dbo.ApiConfig c
    LEFT JOIN dbo.ApiRateLimit rl ON c.ConfigId = rl.ConfigId 
        AND rl.EndpointPattern = @Endpoint 
        AND rl.IsActive = 1
    WHERE c.ConfigId = @ConfigId;
    
    -- Reset counter if more than 1 minute has passed
    IF DATEDIFF(SECOND, @LastReset, GETDATE()) > 60
    BEGIN
        UPDATE dbo.ApiRateLimit
        SET CurrentRequestCount = 1,
            LastResetTime = GETDATE()
        WHERE ConfigId = @ConfigId 
            AND EndpointPattern = @Endpoint;
        
        RETURN 1;
    END
    
    -- Check if limit exceeded
    IF @CurrentCount >= @MaxRequests
        SET @Result = 0;
    
    RETURN @Result;
END
sql
CREATE OR ALTER PROCEDURE dbo.UpdateRateLimit
    @ConfigId INT,
    @Endpoint NVARCHAR(500)
AS
BEGIN
    SET NOCOUNT ON;
    
    IF NOT EXISTS (
        SELECT 1 FROM dbo.ApiRateLimit 
        WHERE ConfigId = @ConfigId AND EndpointPattern = @Endpoint
    )
    BEGIN
        INSERT INTO dbo.ApiRateLimit (ConfigId, EndpointPattern)
        VALUES (@ConfigId, @Endpoint);
    END
    
    UPDATE dbo.ApiRateLimit
    SET CurrentRequestCount = CurrentRequestCount + 1
    WHERE ConfigId = @ConfigId 
        AND EndpointPattern = @Endpoint
        AND DATEDIFF(SECOND, LastResetTime, GETDATE()) <= 60;
END

6. GET API Call with Complex Nested JSON Handling

sql
CREATE OR ALTER PROCEDURE dbo.CallSecureGetApi
    @ConfigId INT,
    @Endpoint NVARCHAR(500),
    @QueryParams NVARCHAR(MAX) = NULL,
    @BatchId UNIQUEIDENTIFIER = NULL OUTPUT,
    @CorrelationId UNIQUEIDENTIFIER = NULL OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET @CorrelationId = ISNULL(@CorrelationId, NEWID());
    SET @BatchId = ISNULL(@BatchId, NEWID());
    
    DECLARE @StartTime DATETIME = GETDATE();
    DECLARE @ErrorMessage NVARCHAR(MAX);
    DECLARE @AccessToken NVARCHAR(MAX);
    DECLARE @TokenType NVARCHAR(50);
    DECLARE @ApiName NVARCHAR(100);
    DECLARE @BaseUrl NVARCHAR(500);
    DECLARE @FullUrl NVARCHAR(2000);
    DECLARE @Headers NVARCHAR(MAX);
    DECLARE @ResponseJson NVARCHAR(MAX);
    DECLARE @ResponseHeaders NVARCHAR(MAX);
    DECLARE @StatusCode INT;
    DECLARE @RequestSize INT;
    DECLARE @ResponseSize INT;
    
    BEGIN TRY
        -- Get API configuration
        SELECT @ApiName = ApiName, @BaseUrl = BaseUrl
        FROM dbo.ApiConfig WHERE ConfigId = @ConfigId;
        
        -- Build URL
        SET @FullUrl = @BaseUrl + @Endpoint;
        IF @QueryParams IS NOT NULL
            SET @FullUrl = @FullUrl + '?' + @QueryParams;
        
        -- Check rate limit
        IF dbo.CheckRateLimit(@ConfigId, @Endpoint) = 0
        BEGIN
            SET @ErrorMessage = 'Rate limit exceeded for endpoint: ' + @Endpoint;
            THROW 50005, @ErrorMessage, 1;
        END
        
        -- Get JWT token if required
        IF EXISTS (SELECT 1 FROM dbo.ApiConfig WHERE ConfigId = @ConfigId AND AuthEndpoint IS NOT NULL)
        BEGIN
            EXEC dbo.GetSecureApiToken
                @ConfigId = @ConfigId,
                @AccessToken = @AccessToken OUTPUT,
                @TokenType = @TokenType OUTPUT,
                @CorrelationId = @CorrelationId OUTPUT,
                @ErrorMessage = @ErrorMessage OUTPUT;
            
            IF @AccessToken IS NULL
                THROW 50006, @ErrorMessage, 1;
                
            SET @Headers = JSON_OBJECT(
                'Content-Type': 'application/json',
                'Authorization': @TokenType + ' ' + @AccessToken,
                'Accept': 'application/json',
                'X-Correlation-ID': CAST(@CorrelationId AS NVARCHAR(50)),
                'User-Agent': 'SQL-Server-2025-API-Client/1.0'
            );
        END
        ELSE
        BEGIN
            SET @Headers = JSON_OBJECT(
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'X-Correlation-ID': CAST(@CorrelationId AS NVARCHAR(50))
            );
        END
        
        -- Update rate limit
        EXEC dbo.UpdateRateLimit @ConfigId, @Endpoint;
        
        -- Make API call
        EXEC sp_invoke_external_rest_endpoint
            @url = @FullUrl,
            @method = 'GET',
            @headers = @Headers,
            @timeout = 45,
            @response = @ResponseJson OUTPUT,
            @response_headers = @ResponseHeaders OUTPUT,
            @response_code = @StatusCode OUTPUT;
        
        SET @ResponseSize = LEN(ISNULL(@ResponseJson, ''));
        
        -- Log audit
        INSERT INTO dbo.ApiAuditLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, StatusCode, RequestSizeBytes,
            ResponseSizeBytes, DurationMs, CorrelationId,
            Success
        ) VALUES (
            @ConfigId, @ApiName, @Endpoint,
            'GET', @StatusCode, @RequestSize,
            @ResponseSize, DATEDIFF(MILLISECOND, @StartTime, GETDATE()),
            @CorrelationId, CASE WHEN @StatusCode BETWEEN 200 AND 299 THEN 1 ELSE 0 END
        );
        
        IF @StatusCode = 200
        BEGIN
            -- Store response
            INSERT INTO dbo.ApiResponseStage (
                BatchId, ConfigId, Endpoint,
                ResponseJson, ResponseHeaders,
                StatusCode, CreatedDate
            ) VALUES (
                @BatchId, @ConfigId, @FullUrl,
                @ResponseJson, @ResponseHeaders,
                @StatusCode, GETDATE()
            );
            
            -- Return success
            SELECT 
                @CorrelationId AS CorrelationId,
                @BatchId AS BatchId,
                @StatusCode AS StatusCode,
                'Success' AS Status,
                @ResponseSize AS ResponseSizeBytes,
                DATEDIFF(MILLISECOND, @StartTime, GETDATE()) AS DurationMs;
        END
        ELSE
        BEGIN
            -- Log error
            INSERT INTO dbo.ApiErrorLog (
                ConfigId, ApiName, Endpoint,
                HttpMethod, ErrorMessage, StatusCode,
                ResponseBody, ResponseHeaders, RequestHeaders,
                CorrelationId, BatchId
            ) VALUES (
                @ConfigId, @ApiName, @FullUrl,
                'GET', 'API call failed', @StatusCode,
                LEFT(@ResponseJson, 4000), @ResponseHeaders, @Headers,
                @CorrelationId, @BatchId
            );
            
            THROW 50007, 'API call failed with status: ' + CAST(@StatusCode AS NVARCHAR(10)), 1;
        END
        
    END TRY
    BEGIN CATCH
        SET @ErrorMessage = ERROR_MESSAGE();
        
        INSERT INTO dbo.ApiErrorLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, ErrorMessage, StackTrace,
            CorrelationId, BatchId
        ) VALUES (
            @ConfigId, @ApiName, @Endpoint,
            'GET', @ErrorMessage, 
            ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10)),
            @CorrelationId, @BatchId
        );
        
        THROW;
    END CATCH
END

7. POST API Call with Complex JSON

sql
CREATE OR ALTER PROCEDURE dbo.CallSecurePostApi
    @ConfigId INT,
    @Endpoint NVARCHAR(500),
    @JsonPayload NVARCHAR(MAX),
    @BatchId UNIQUEIDENTIFIER = NULL OUTPUT,
    @CorrelationId UNIQUEIDENTIFIER = NULL OUTPUT,
    @ResponseJson NVARCHAR(MAX) = NULL OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET @CorrelationId = ISNULL(@CorrelationId, NEWID());
    SET @BatchId = ISNULL(@BatchId, NEWID());
    
    DECLARE @StartTime DATETIME = GETDATE();
    DECLARE @ErrorMessage NVARCHAR(MAX);
    DECLARE @AccessToken NVARCHAR(MAX);
    DECLARE @TokenType NVARCHAR(50);
    DECLARE @ApiName NVARCHAR(100);
    DECLARE @BaseUrl NVARCHAR(500);
    DECLARE @FullUrl NVARCHAR(2000);
    DECLARE @Headers NVARCHAR(MAX);
    DECLARE @ResponseHeaders NVARCHAR(MAX);
    DECLARE @StatusCode INT;
    DECLARE @RequestSize INT = LEN(@JsonPayload);
    DECLARE @ResponseSize INT;
    
    BEGIN TRY
        -- Validate JSON
        IF ISJSON(@JsonPayload) = 0
        BEGIN
            SET @ErrorMessage = 'Invalid JSON payload';
            THROW 50008, @ErrorMessage, 1;
        END
        
        -- Get API configuration
        SELECT @ApiName = ApiName, @BaseUrl = BaseUrl
        FROM dbo.ApiConfig WHERE ConfigId = @ConfigId;
        
        SET @FullUrl = @BaseUrl + @Endpoint;
        
        -- Check rate limit
        IF dbo.CheckRateLimit(@ConfigId, @Endpoint) = 0
        BEGIN
            SET @ErrorMessage = 'Rate limit exceeded for endpoint: ' + @Endpoint;
            THROW 50009, @ErrorMessage, 1;
        END
        
        -- Get JWT token if required
        IF EXISTS (SELECT 1 FROM dbo.ApiConfig WHERE ConfigId = @ConfigId AND AuthEndpoint IS NOT NULL)
        BEGIN
            EXEC dbo.GetSecureApiToken
                @ConfigId = @ConfigId,
                @AccessToken = @AccessToken OUTPUT,
                @TokenType = @TokenType OUTPUT,
                @CorrelationId = @CorrelationId OUTPUT,
                @ErrorMessage = @ErrorMessage OUTPUT;
            
            IF @AccessToken IS NULL
                THROW 50010, @ErrorMessage, 1;
                
            SET @Headers = JSON_OBJECT(
                'Content-Type': 'application/json',
                'Authorization': @TokenType + ' ' + @AccessToken,
                'Accept': 'application/json',
                'X-Correlation-ID': CAST(@CorrelationId AS NVARCHAR(50)),
                'User-Agent': 'SQL-Server-2025-API-Client/1.0'
            );
        END
        ELSE
        BEGIN
            SET @Headers = JSON_OBJECT(
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'X-Correlation-ID': CAST(@CorrelationId AS NVARCHAR(50))
            );
        END
        
        -- Update rate limit
        EXEC dbo.UpdateRateLimit @ConfigId, @Endpoint;
        
        -- Make API call
        EXEC sp_invoke_external_rest_endpoint
            @url = @FullUrl,
            @method = 'POST',
            @headers = @Headers,
            @payload = @JsonPayload,
            @timeout = 60,
            @response = @ResponseJson OUTPUT,
            @response_headers = @ResponseHeaders OUTPUT,
            @response_code = @StatusCode OUTPUT;
        
        SET @ResponseSize = LEN(ISNULL(@ResponseJson, ''));
        
        -- Log audit
        INSERT INTO dbo.ApiAuditLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, StatusCode, RequestSizeBytes,
            ResponseSizeBytes, DurationMs, CorrelationId,
            Success
        ) VALUES (
            @ConfigId, @ApiName, @Endpoint,
            'POST', @StatusCode, @RequestSize,
            @ResponseSize, DATEDIFF(MILLISECOND, @StartTime, GETDATE()),
            @CorrelationId, CASE WHEN @StatusCode BETWEEN 200 AND 299 THEN 1 ELSE 0 END
        );
        
        IF @StatusCode BETWEEN 200 AND 299
        BEGIN
            -- Store response
            INSERT INTO dbo.ApiResponseStage (
                BatchId, ConfigId, Endpoint,
                ResponseJson, ResponseHeaders,
                StatusCode, CreatedDate
            ) VALUES (
                @BatchId, @ConfigId, @FullUrl,
                @ResponseJson, @ResponseHeaders,
                @StatusCode, GETDATE()
            );
            
            -- Return success
            SELECT 
                @CorrelationId AS CorrelationId,
                @BatchId AS BatchId,
                @StatusCode AS StatusCode,
                'Success' AS Status,
                @RequestSize AS RequestSizeBytes,
                @ResponseSize AS ResponseSizeBytes,
                DATEDIFF(MILLISECOND, @StartTime, GETDATE()) AS DurationMs,
                @ResponseJson AS ResponseJson;
        END
        ELSE
        BEGIN
            -- Log error
            INSERT INTO dbo.ApiErrorLog (
                ConfigId, ApiName, Endpoint,
                HttpMethod, ErrorMessage, StatusCode,
                ResponseBody, ResponseHeaders, RequestHeaders,
                RequestPayload, CorrelationId, BatchId
            ) VALUES (
                @ConfigId, @ApiName, @FullUrl,
                'POST', 'API call failed', @StatusCode,
                LEFT(@ResponseJson, 4000), @ResponseHeaders, @Headers,
                LEFT(@JsonPayload, 4000), @CorrelationId, @BatchId
            );
            
            THROW 50011, 'API call failed with status: ' + CAST(@StatusCode AS NVARCHAR(10)), 1;
        END
        
    END TRY
    BEGIN CATCH
        SET @ErrorMessage = ERROR_MESSAGE();
        
        INSERT INTO dbo.ApiErrorLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, ErrorMessage, StackTrace,
            RequestPayload, CorrelationId, BatchId
        ) VALUES (
            @ConfigId, @ApiName, @Endpoint,
            'POST', @ErrorMessage, 
            ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10)),
            LEFT(@JsonPayload, 4000), @CorrelationId, @BatchId
        );
        
        THROW;
    END CATCH
END

8. Process Complex Nested JSON from Free APIs

sql
CREATE OR ALTER PROCEDURE dbo.ProcessJSONPlaceholderData
    @BatchId UNIQUEIDENTIFIER,
    @CorrelationId UNIQUEIDENTIFIER = NULL
AS
BEGIN
    SET NOCOUNT ON;
    SET @CorrelationId = ISNULL(@CorrelationId, NEWID());
    
    DECLARE @ErrorMessage NVARCHAR(MAX);
    DECLARE @ProcessedCount INT = 0;
    DECLARE @ErrorCount INT = 0;
    
    BEGIN TRY
        -- Process users with nested data
        ;WITH UserData AS (
            SELECT 
                StageId,
                JSON_VALUE(ResponseJson, '$.id') AS UserId,
                JSON_VALUE(ResponseJson, '$.name') AS UserName,
                JSON_VALUE(ResponseJson, '$.username') AS Username,
                JSON_VALUE(ResponseJson, '$.email') AS Email,
                JSON_VALUE(ResponseJson, '$.address.street') AS AddressStreet,
                JSON_VALUE(ResponseJson, '$.address.suite') AS AddressSuite,
                JSON_VALUE(ResponseJson, '$.address.city') AS AddressCity,
                JSON_VALUE(ResponseJson, '$.address.zipcode') AS AddressZipCode,
                JSON_VALUE(ResponseJson, '$.address.geo.lat') AS GeoLat,
                JSON_VALUE(ResponseJson, '$.address.geo.lng') AS GeoLng,
                JSON_VALUE(ResponseJson, '$.phone') AS Phone,
                JSON_VALUE(ResponseJson, '$.website') AS Website,
                JSON_VALUE(ResponseJson, '$.company.name') AS CompanyName,
                JSON_VALUE(ResponseJson, '$.company.catchPhrase') AS CompanyCatchPhrase,
                JSON_VALUE(ResponseJson, '$.company.bs') AS CompanyBs,
                ResponseJson AS RawResponse
            FROM dbo.ApiResponseStage
            WHERE BatchId = @BatchId
                AND Endpoint LIKE '%/users/%'
                AND ISJSON(ResponseJson) = 1
        )
        MERGE dbo.UsersData AS target
        USING UserData AS source
        ON target.UserId = source.UserId
        WHEN MATCHED THEN
            UPDATE SET 
                Email = source.Email,
                FirstName = CASE 
                    WHEN CHARINDEX(' ', source.UserName) > 0 
                    THEN LEFT(source.UserName, CHARINDEX(' ', source.UserName) - 1)
                    ELSE source.UserName
                END,
                LastName = CASE 
                    WHEN CHARINDEX(' ', source.UserName) > 0 
                    THEN SUBSTRING(source.UserName, CHARINDEX(' ', source.UserName) + 1, LEN(source.UserName))
                    ELSE ''
                END,
                AddressStreet = source.AddressStreet,
                AddressSuite = source.AddressSuite,
                AddressCity = source.AddressCity,
                AddressZipCode = source.AddressZipCode,
                AddressGeoLat = TRY_CAST(source.GeoLat AS DECIMAL(10,6)),
                AddressGeoLng = TRY_CAST(source.GeoLng AS DECIMAL(10,6)),
                Phone = source.Phone,
                Website = source.Website,
                CompanyName = source.CompanyName,
                CompanyCatchPhrase = source.CompanyCatchPhrase,
                CompanyBs = source.CompanyBs,
                RawResponse = source.RawResponse,
                LastSynced = GETDATE()
        WHEN NOT MATCHED THEN
            INSERT (
                UserId, Email, FirstName, LastName,
                AddressStreet, AddressSuite, AddressCity, AddressZipCode,
                AddressGeoLat, AddressGeoLng, Phone, Website,
                CompanyName, CompanyCatchPhrase, CompanyBs, RawResponse
            ) VALUES (
                source.UserId, source.Email,
                CASE 
                    WHEN CHARINDEX(' ', source.UserName) > 0 
                    THEN LEFT(source.UserName, CHARINDEX(' ', source.UserName) - 1)
                    ELSE source.UserName
                END,
                CASE 
                    WHEN CHARINDEX(' ', source.UserName) > 0 
                    THEN SUBSTRING(source.UserName, CHARINDEX(' ', source.UserName) + 1, LEN(source.UserName))
                    ELSE ''
                END,
                source.AddressStreet, source.AddressSuite, source.AddressCity, source.AddressZipCode,
                TRY_CAST(source.GeoLat AS DECIMAL(10,6)), TRY_CAST(source.GeoLng AS DECIMAL(10,6)),
                source.Phone, source.Website,
                source.CompanyName, source.CompanyCatchPhrase, source.CompanyBs, source.RawResponse
            );
        
        SET @ProcessedCount = @@ROWCOUNT;
        
        -- Process posts for each user
        INSERT INTO dbo.UsersPosts (UserId, PostId, Title, Body, BatchId)
        SELECT 
            JSON_VALUE(value, '$.userId') AS UserId,
            JSON_VALUE(value, '$.id') AS PostId,
            JSON_VALUE(value, '$.title') AS Title,
            JSON_VALUE(value, '$.body') AS Body,
            @BatchId
        FROM dbo.ApiResponseStage s
        CROSS APPLY OPENJSON(s.ResponseJson) 
        WHERE s.BatchId = @BatchId
            AND s.Endpoint LIKE '%/posts'
            AND ISJSON(s.ResponseJson) = 1;
        
        -- Process todos for each user
        INSERT INTO dbo.UsersTodos (UserId, TodoId, Title, Completed, BatchId)
        SELECT 
            JSON_VALUE(value, '$.userId') AS UserId,
            JSON_VALUE(value, '$.id') AS TodoId,
            JSON_VALUE(value, '$.title') AS Title,
            JSON_VALUE(value, '$.completed') AS Completed,
            @BatchId
        FROM dbo.ApiResponseStage s
        CROSS APPLY OPENJSON(s.ResponseJson) 
        WHERE s.BatchId = @BatchId
            AND s.Endpoint LIKE '%/todos'
            AND ISJSON(s.ResponseJson) = 1;
        
        -- Update staging table
        UPDATE dbo.ApiResponseStage
        SET IsProcessed = 1,
            ProcessedDate = GETDATE()
        WHERE BatchId = @BatchId;
        
        -- Log success
        INSERT INTO dbo.ApiAuditLog (
            ConfigId, ApiName, Endpoint,
            HttpMethod, StatusCode, DurationMs,
            CorrelationId, Success
        ) VALUES (
            (SELECT TOP 1 ConfigId FROM dbo.ApiResponseStage WHERE BatchId = @BatchId),
            'JSONPlaceholder', 'ProcessData',
            'PROCESS', 200, DATEDIFF(MILLISECOND, GETDATE(), GETDATE()),
            @CorrelationId, 1
        );
        
        PRINT 'Successfully processed ' + CAST(@ProcessedCount AS NVARCHAR) + ' users';
        
    END TRY
    BEGIN CATCH
        SET @ErrorMessage = ERROR_MESSAGE();
        SET @ErrorCount = @ErrorCount + 1;
        
        INSERT INTO dbo.ApiErrorLog (
            ApiName, Endpoint, HttpMethod,
            ErrorMessage, StackTrace, CorrelationId,
            BatchId
        ) VALUES (
            'JSONPlaceholder', 'ProcessData', 'PROCESS',
            @ErrorMessage, 
            ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10)),
            @CorrelationId, @BatchId
        );
        
        THROW;
    END CATCH
END

9. Complete Example Usage

A. GET Request to JSONPlaceholder

sql
-- Example 1: GET users with nested data
DECLARE @BatchId UNIQUEIDENTIFIER, @CorrelationId UNIQUEIDENTIFIER;

EXEC dbo.CallSecureGetApi
    @ConfigId = (SELECT ConfigId FROM dbo.ApiConfig WHERE ApiName = 'JSONPlaceholder'),
    @Endpoint = 'users',
    @QueryParams = '_limit=10',
    @BatchId = @BatchId OUTPUT,
    @CorrelationId = @CorrelationId OUTPUT;

-- Process the nested JSON data
EXEC dbo.ProcessJSONPlaceholderData @BatchId = @BatchId;

-- View processed data
SELECT TOP 10 * FROM dbo.UsersData ORDER BY UserId;

B. GET Request with JWT Authentication (ReqRes.in)

sql
-- Example 2: GET with JWT authentication
DECLARE @BatchId2 UNIQUEIDENTIFIER, @CorrelationId2 UNIQUEIDENTIFIER;

-- First, authenticate to get token (automatically handled)
EXEC dbo.CallSecureGetApi
    @ConfigId = (SELECT ConfigId FROM dbo.ApiConfig WHERE ApiName = 'ReqResAPI'),
    @Endpoint = 'users',
    @QueryParams = 'page=2',
    @BatchId = @BatchId2 OUTPUT,
    @CorrelationId = @CorrelationId2 OUTPUT;

-- View the response
SELECT TOP 5 
    JSON_VALUE(ResponseJson, '$.data[0].id') AS FirstUserId,
    JSON_VALUE(ResponseJson, '$.data[0].email') AS FirstUserEmail,
    JSON_VALUE(ResponseJson, '$.page') AS Page,
    JSON_VALUE(ResponseJson, '$.total_pages') AS TotalPages
FROM dbo.ApiResponseStage
WHERE BatchId = @BatchId2;

C. POST Request with Complex JSON

sql
-- Example 3: POST with nested JSON payload
DECLARE @BatchId3 UNIQUEIDENTIFIER, @CorrelationId3 UNIQUEIDENTIFIER, @ResponseJson NVARCHAR(MAX);

DECLARE @JsonPayload NVARCHAR(MAX) = JSON_OBJECT(
    'name': 'John Doe',
    'job': 'Software Engineer',
    'contact': JSON_OBJECT(
        'email': 'john.doe@example.com',
        'phone': '+1234567890'
    ),
    'address': JSON_OBJECT(
        'street': '123 Main St',
        'city': 'New York',
        'state': 'NY',
        'zip': '10001'
    ),
    'skills': JSON_ARRAY('C#', 'SQL', 'JavaScript', 'Python')
);

EXEC dbo.CallSecurePostApi
    @ConfigId = (SELECT ConfigId FROM dbo.ApiConfig WHERE ApiName = 'ReqResAPI'),
    @Endpoint = 'users',
    @JsonPayload = @JsonPayload,
    @BatchId = @BatchId3 OUTPUT,
    @CorrelationId = @CorrelationId3 OUTPUT,
    @ResponseJson = @ResponseJson OUTPUT;

-- View response
SELECT 
    JSON_VALUE(@ResponseJson, '$.id') AS CreatedId,
    JSON_VALUE(@ResponseJson, '$.name') AS Name,
    JSON_VALUE(@ResponseJson, '$.job') AS Job,
    JSON_VALUE(@ResponseJson, '$.createdAt') AS CreatedAt;

10. Monitoring and Dashboard Views

sql
-- Comprehensive monitoring view
CREATE OR ALTER VIEW dbo.vw_ApiHealthDashboard
AS
WITH ApiStats AS (
    SELECT 
        c.ApiName,
        c.BaseUrl,
        COUNT(DISTINCT s.BatchId) AS TotalBatches,
        COUNT(s.StageId) AS TotalRequests,
        SUM(CASE WHEN s.StatusCode BETWEEN 200 AND 299 THEN 1 ELSE 0 END) AS SuccessfulRequests,
        SUM(CASE WHEN s.StatusCode >= 400 THEN 1 ELSE 0 END) AS FailedRequests,
        AVG(DATEDIFF(MILLISECOND, s.CreatedDate, ISNULL(s.ProcessedDate, GETDATE()))) AS AvgProcessingTimeMs,
        MIN(s.CreatedDate) AS FirstRequest,
        MAX(s.CreatedDate) AS LastRequest
    FROM dbo.ApiConfig c
    LEFT JOIN dbo.ApiResponseStage s ON c.ConfigId = s.ConfigId
    GROUP BY c.ConfigId, c.ApiName, c.BaseUrl
),
ErrorStats AS (
    SELECT 
        c.ApiName,
        COUNT(e.ErrorId) AS TotalErrors,
        COUNT(CASE WHEN e.Resolved = 0 THEN 1 END) AS UnresolvedErrors,
        MIN(e.ErrorTime) AS FirstError,
        MAX(e.ErrorTime) AS LastError
    FROM dbo.ApiConfig c
    LEFT JOIN dbo.ApiErrorLog e ON c.ConfigId = e.ConfigId
    GROUP BY c.ConfigId, c.ApiName
),
RateLimitStats AS (
    SELECT 
        c.ApiName,
        rl.EndpointPattern,
        rl.MaxRequestsPerMinute,
        rl.CurrentRequestCount,
        rl.LastResetTime
    FROM dbo.ApiConfig c
    LEFT JOIN dbo.ApiRateLimit rl ON c.ConfigId = rl.ConfigId
)
SELECT 
    s.ApiName,
    s.BaseUrl,
    s.TotalBatches,
    s.TotalRequests,
    s.SuccessfulRequests,
    s.FailedRequests,
    s.AvgProcessingTimeMs,
    s.FirstRequest,
    s.LastRequest,
    e.TotalErrors,
    e.UnresolvedErrors,
    e.FirstError,
    e.LastError,
    CASE 
        WHEN s.TotalRequests > 0 
        THEN CAST((s.SuccessfulRequests * 100.0 / s.TotalRequests) AS DECIMAL(5,2))
        ELSE 100.0
    END AS SuccessRate,
    DATEDIFF(HOUR, s.LastRequest, GETDATE()) AS HoursSinceLastRequest
FROM ApiStats s
LEFT JOIN ErrorStats e ON s.ApiName = e.ApiName;

11. Cleanup and Maintenance Job

sql
CREATE OR ALTER PROCEDURE dbo.CleanupApiData
    @RetentionDays INT = 30,
    @BatchSize INT = 5000,
    @DryRun BIT = 0
AS
BEGIN
    SET NOCOUNT ON;
    
    DECLARE @CutoffDate DATETIME = DATEADD(DAY, -@RetentionDays, GETDATE());
    DECLARE @RowsDeleted INT;
    DECLARE @TotalDeleted INT = 0;
    DECLARE @Message NVARCHAR(MAX);
    
    IF @DryRun = 1
    BEGIN
        -- Show what would be deleted
        SELECT 
            'ApiResponseStage' AS TableName,
            COUNT(*) AS RecordsToDelete
        FROM dbo.ApiResponseStage
        WHERE CreatedDate < @CutoffDate
          AND IsProcessed = 1
        
        UNION ALL
        
        SELECT 
            'ApiErrorLog' AS TableName,
            COUNT(*) AS RecordsToDelete
        FROM dbo.ApiErrorLog
        WHERE ErrorTime < @CutoffDate
          AND Resolved = 1
        
        UNION ALL
        
        SELECT 
            'ApiAuditLog' AS TableName,
            COUNT(*) AS RecordsToDelete
        FROM dbo.ApiAuditLog
        WHERE CalledAt < @CutoffDate;
        
        RETURN;
    END
    
    BEGIN TRANSACTION;
    
    BEGIN TRY
        -- Cleanup ApiResponseStage in batches
        SET @RowsDeleted = 1;
        WHILE @RowsDeleted > 0
        BEGIN
            DELETE TOP (@BatchSize)
            FROM dbo.ApiResponseStage
            WHERE CreatedDate < @CutoffDate
              AND IsProcessed = 1;
            
            SET @RowsDeleted = @@ROWCOUNT;
            SET @TotalDeleted = @TotalDeleted + @RowsDeleted;
            
            WAITFOR DELAY '00:00:00.05';
        END
        
        SET @Message = 'Deleted ' + CAST(@TotalDeleted AS NVARCHAR) + ' records from ApiResponseStage';
        PRINT @Message;
        
        -- Cleanup resolved errors
        DELETE FROM dbo.ApiErrorLog
        WHERE ErrorTime < @CutoffDate
          AND Resolved = 1;
        
        SET @Message = 'Deleted ' + CAST(@@ROWCOUNT AS NVARCHAR) + ' records from ApiErrorLog';
        PRINT @Message;
        
        -- Cleanup old audit logs
        DELETE FROM dbo.ApiAuditLog
        WHERE CalledAt < @CutoffDate;
        
        SET @Message = 'Deleted ' + CAST(@@ROWCOUNT AS NVARCHAR) + ' records from ApiAuditLog';
        PRINT @Message;
        
        COMMIT TRANSACTION;
        
        -- Log cleanup
        INSERT INTO dbo.ApiAuditLog (
            ApiName, Endpoint, HttpMethod,
            StatusCode, DurationMs, Success
        ) VALUES (
            'System', 'Cleanup', 'DELETE',
            200, 0, 1
        );
        
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;
        
        DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE();
        
        INSERT INTO dbo.ApiErrorLog (
            ApiName, Endpoint, HttpMethod,
            ErrorMessage, StackTrace
        ) VALUES (
            'System', 'Cleanup', 'DELETE',
            @ErrorMessage, 
            ERROR_PROCEDURE() + ' Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10))
        );
        
        THROW;
    END CATCH
END

Key Security Features:

  1. Encryption at Rest: All sensitive data (tokens, credentials) encrypted using SQL Server encryption

  2. JWT Management: Secure token acquisition, storage, and refresh with encryption

  3. Rate Limiting: Prevents API abuse and respects API limits

  4. Comprehensive Logging: Full audit trail for compliance and debugging

  5. Error Handling: Robust error capture with correlation IDs for tracing

  6. Parameterized Inputs: Protection against SQL injection

  7. Timeouts: Prevents hanging requests

  8. Retry Logic: Exponential backoff for transient failures

  9. Correlation IDs: End-to-end request tracing

  10. Compression: Efficient storage of large JSON responses

Real API Examples Used:

  1. JSONPlaceholderhttps://jsonplaceholder.typicode.com/

    • /users - Returns nested user data with addresses and companies

    • /posts - User posts data

    • /todos - User todos data

  2. ReqRes.inhttps://reqres.in/api/

    • /login - JWT authentication endpoint

    • /users - User data with pagination

    • /users/{id} - Single user with nested data

Best Use Cases

Enterprise Scenarios Where This Excels:

1. E-commerce Data Synchronization

sql
-- Sync customer orders from multiple marketplaces
EXEC dbo.OrchestrateApiDataSync
    @ConfigId = 1,
    @Endpoint = '/orders',
    @SinceDate = DATEADD(HOUR, -1, GETDATE());

2. Financial Data Aggregation

  • Real-time stock price updates

  • Currency exchange rate synchronization

  • Transaction processing from banking APIs

3. IoT Data Ingestion

sql
-- Process sensor data from IoT devices
INSERT INTO SensorReadings
SELECT 
    JSON_VALUE(value, '$.deviceId'),
    JSON_VALUE(value, '$.temperature'),
    JSON_VALUE(value, '$.timestamp')
FROM OPENJSON(@ResponseJson, '$.readings');

4. Customer Relationship Management

  • Sync contact data from CRM systems

  • Update customer profiles in real-time

  • Track customer interactions across platforms

5. Supply Chain Integration

  • Inventory level synchronization

  • Order status updates

  • Shipping tracking integration


7. Handling Complex Scenarios

7.1 Pagination Strategies

sql
-- Handle APIs with different pagination models
DECLARE @CurrentPage INT = 1;
DECLARE @HasMore BIT = 1;

WHILE @HasMore = 1 AND @CurrentPage <= @MaxPages
BEGIN
    -- Build URL with appropriate pagination params
    SET @PageUrl = @BaseUrl + @Endpoint + 
        CASE @PaginationType
            WHEN 'page_based' THEN '?page=' + CAST(@CurrentPage AS NVARCHAR)
            WHEN 'offset_based' THEN '?offset=' + CAST((@CurrentPage-1)*@PageSize AS NVARCHAR)
            WHEN 'cursor_based' THEN '?cursor=' + @NextCursor
        END;
END

7.2 Rate Limiting & Throttling

sql
-- Implement adaptive rate limiting
IF @StatusCode = 429  -- Too Many Requests
BEGIN
    DECLARE @RetryAfter INT = ISNULL(
        JSON_VALUE(@ResponseHeaders, '$.Retry-After'), 
        60
    );
    WAITFOR DELAY '00:00:' + RIGHT('00' + CAST(@RetryAfter AS VARCHAR(2)), 2);
END

7.3 Data Transformation Pipelines

sql
-- Transform API data for different consumers
;WITH TransformedData AS (
    SELECT 
        JSON_VALUE(value, '$.id') AS SourceId,
        UPPER(JSON_VALUE(value, '$.name')) AS NormalizedName,
        FORMAT(TRY_CAST(JSON_VALUE(value, '$.date') AS DATETIME), 'yyyy-MM-dd') AS StandardDate,
        CASE 
            WHEN JSON_VALUE(value, '$.status') = 'active' THEN 1
            ELSE 0 
        END AS IsActive
    FROM OPENJSON(@ResponseJson, '$.items')
)
MERGE TargetTable AS target
USING TransformedData AS source...

7.4 Circuit Breaker Pattern

sql
-- Implement circuit breaker for unreliable APIs
IF (SELECT FailureCount FROM ApiHealth WHERE ConfigId = @ConfigId) > @Threshold
BEGIN
    -- Circuit open - fail fast
    RAISERROR('API circuit breaker open - too many failures', 16, 1);
    RETURN;
END

8. Pros & Cons Analysis

Advantages ✅

Performance Benefits:

  • Reduced Network Latency: Direct database-to-API calls eliminate middle-tier overhead

  • Bulk Processing: Native SQL operations on large datasets

  • Connection Pooling: Reuse of database connections for API calls

Development Efficiency:

  • Single Codebase: SQL-based logic reduces technology stack complexity

  • Transactional Integrity: ACID compliance for API operations

  • Simplified Testing: All logic testable within SQL environment

Operational Benefits:

  • Centralized Management: Single point for API configuration and monitoring

  • Built-in Security: Leverage SQL Server security features

  • Scalability: Built on proven SQL Server infrastructure

Challenges & Limitations ❌

Technical Constraints:

  • Timeout Handling: Limited to SQL Server timeout configurations

  • Memory Usage: Large JSON responses can consume significant memory

  • Connection Limits: Shared database connection pool constraints

Development Considerations:

  • SQL Expertise Required: Developers need strong SQL skills

  • Limited Debugging: Traditional debugging tools less effective

  • Version Control: Managing SQL scripts vs. application code

Architectural Concerns:

  • Database Coupling: Tight coupling between business logic and database

  • Scalability Limits: Horizontal scaling challenges

  • Vendor Lock-in: Specific to SQL Server ecosystem

9. Security Measures & Do's/Don'ts

Critical Security Implementations:

DO Implement:

sql
✅ Encryption at Rest
OPEN SYMMETRIC KEY ApiSymmetricKey
DECRYPTION BY CERTIFICATE ApiEncryptionCert;

✅ Rate Limiting
CREATE TABLE dbo.ApiRateLimit (
    MaxRequestsPerMinute INT DEFAULT 60,
    CurrentRequestCount INT
);

✅ Comprehensive Auditing
CREATE TABLE dbo.ApiAuditLog (
    CorrelationId UNIQUEIDENTIFIER,
    CalledBy SYSNAME,
    RequestHeaders NVARCHAR(MAX)
);

✅ Input Validation
IF ISJSON(@JsonPayload) = 0
    THROW 50008, 'Invalid JSON payload', 1;

DON'T Do:

sql
❌ Hardcode Credentials
-- DON'T: INSERT INTO Config VALUES ('token', 'my-secret-key');
-- DO: Use encrypted storageIgnore Timeouts
-- DON'T: @timeout = 0 (infinite)
-- DO: @timeout = 30 (reasonable limit)

❌ Skip Error Handling
-- DON'T: Assume API calls always succeed
-- DO: Implement comprehensive try-catch blocks

❌ Expose Sensitive Data
-- DON'T: SELECT * FROM ApiTokenStore
-- DO: SELECT TokenType, ExpiresAt FROM ApiTokenStore

Security Checklist:

  • All secrets encrypted using SQL Server encryption

  • JWT tokens validated and refreshed automatically

  • Rate limiting implemented per endpoint

  • Comprehensive audit logging enabled

  • Input validation for all parameters

  • Error messages sanitized for production

  • Regular security reviews scheduled

  • Access controls properly configured


10. Advanced Implementation Patterns

10.1 Async Processing with Service Broker

sql
-- Implement async API processing
BEGIN CONVERSATION TIMER @ConversationHandle
TIMEOUT = 30;  -- Process after 30 seconds

-- Queue API requests for background processing
SEND ON CONVERSATION @ConversationHandle
MESSAGE TYPE [ApiRequest] (@JsonPayload);

10.2 Caching Strategy

sql
-- Implement API response caching
CREATE TABLE dbo.ApiResponseCache (
    CacheKey NVARCHAR(500) PRIMARY KEY,
    ResponseJson NVARCHAR(MAX),
    ExpiresAt DATETIME,
    CreatedAt DATETIME DEFAULT GETDATE()
);

-- Check cache before API call
SELECT @CachedResponse = ResponseJson
FROM dbo.ApiResponseCache
WHERE CacheKey = @CacheKey
  AND ExpiresAt > GETDATE();

10.3 Circuit Breaker Implementation

sql
-- Track API health
CREATE TABLE dbo.ApiCircuitBreaker (
    ConfigId INT PRIMARY KEY,
    FailureCount INT DEFAULT 0,
    LastFailureTime DATETIME,
    CircuitState VARCHAR(20) DEFAULT 'CLOSED',
    LastStateChange DATETIME
);

-- Check circuit state before API call
IF (SELECT CircuitState FROM ApiCircuitBreaker WHERE ConfigId = @ConfigId) = 'OPEN'
    AND DATEADD(MINUTE, 5, LastStateChange) > GETDATE()
BEGIN
    -- Circuit is open - fail fast
    RETURN;
END

11. Conclusion & Final Thoughts

The Future of Database-Driven API Integration

SQL Server 2025 represents a paradigm shift in how we approach API integration. By bringing REST API capabilities directly into the database layer, organizations can build more efficient, secure, and maintainable data pipelines.

Key Takeaways:

  1. Security First: Always implement encryption, rate limiting, and comprehensive logging

  2. Resilience Matters: Build systems that gracefully handle API failures

  3. Monitor Everything: Visibility is crucial for production systems

  4. Start Simple: Begin with basic integrations before implementing complex patterns

  5. Test Thoroughly: Use free APIs for development and testing

Your Next Steps:

  1. Experiment: Set up a test environment with free APIs

  2. Implement: Start with basic GET requests and progress to complex scenarios

  3. Monitor: Build your monitoring dashboard early

  4. Optimize: Continuously refine based on performance metrics

  5. Share: Contribute your learnings back to the community

Free Resources from FreeLearning365.com:

  • Download complete SQL scripts from our GitHub repository

  • Join our community forum for support and discussions

  • Access video tutorials on advanced implementation patterns

  • Subscribe to our newsletter for weekly SQL Server tips

Final Note from FreeLearning365.com:
This comprehensive guide represents hundreds of hours of research and practical implementation. We believe in democratizing knowledge and making advanced technical skills accessible to everyone. Whether you're a beginner or an experienced professional, there's always something new to learn in the ever-evolving world of technology.

Stay curious, keep learning, and remember: The best investment you can make is in your own knowledge.


💡 Ready to Implement?
Download our complete SQL script package and start building your secure API integration system today! Visit FreeLearning365.com/ for all materials mentioned in this guide.

Share your implementation experiences with our community using #SQLServerAPI #FreeLearning365



Post a Comment

0 Comments