top of page

Snowflake Native Apps framework: Porting an existing API to Snowflake

Writer: Pedram BirounvandPedram Birounvand

What is the Snowflake Native Apps framework?

The Snowflake native app framework (currently in preview for AWS-accounts) allows users to create and share data-applications with other snowflake accounts. The applications can be as simple as a custom stored procedure or an entire app with a Streamlit interface and custom tables.


Building a Snowflake Native App with external access table of contents:

💡 You currently have to have a snowflake account with AWS as a cloud provider in a non gov-region.


External access in Snowflake:

  • Creating a secret in snowflake

  • Setting up a network rule in snowflake

  • Setting up an external access integration in snowflake

  • Setting up an external function in snowflake


Creating a secret in snowflake (Optional)

CREATE SECRET MY_API_SECRET
    TYPE = GENERIC_STRING
    SECRET_STRING = '<your-api-key>';

Setting up a network rule in snowflake

CREATE NETWORK RULE MY_NETWORK_RULE
    MODE = EGRESS
    TYPE = HOST_PORT
    VALUE_LIST = ('<external-domain>');

Setting up an external access integration in snowflake

CREATE EXTERNAL ACCESS INTEGRATION MY_EXTERNAL_INTEGRATION
    ALLOWED_NETWORK_RULES = (MY_NETWORK_RULE)
    ALLOWED_AUTHENTICATION_SECRETS = (MY_API_SECRET)
    ENABLED = true;

Setting up an external function in snowflake

//Example function of a get-request with a single argument and the api-key as bearer token
CREATE SECURE FUNCTION CALL_YOUR_API(argument varchar)
    RETURNS STRING
    LANGUAGE PYTHON
    RUNTIME_VERSION = 3.9
    HANDLER = 'call_api'
    EXTERNAL_ACCESS_INTEGRATIONS = (MY_EXTERNAL_INTEGRATION)
    SECRETS = ('API_KEY' = MY_API_SECRET)
    PACKAGES = ('requests','urllib3')
    AS
$$
import _snowflake
import requests
from urllib.parse import urlencode
def call_api(argument):
    my_api_key = _snowflake.get_generic_secret_string('API_KEY')
    request_url = "<url-to-request>"
    headers = {
        "accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"Bearer {my_api_key}",
    }
    response = requests.get(request_url + "?" + urlencode({"argument":argument}),headers=headers)
    return response.text
$$;

Calling the function from snowflake

select CALL_YOUR_API('my argument');

Building the app

App hierarchy

readme.md
manifest.yml
streamlit
|-- app.py
|-- environment.yml
scripts
|-- setup.sql

Creating a manifest-file

manifest_version: 1
artifacts:
  setup_script: scripts/setup.sql
  readme: readme.md
  extension_code: true

Creating a setup-script

CREATE APPLICATION ROLE app_public;

CREATE OR ALTER VERSIONED SCHEMA code_schema;
GRANT USAGE ON SCHEMA code_schema TO APPLICATION ROLE app_public;

CREATE STREAMLIT code_schema.your_streamlit_app
  FROM '/streamlit'
  MAIN_FILE = '/app.py'
;

GRANT USAGE ON STREAMLIT code_schema.your_streamlit_app TO APPLICATION ROLE app_public;

Creating a streamlit app

import streamlit as st
from snowflake.snowpark.context import get_active_session
import snowflake.permissions as permission
import json

st.title("Hello world")

# Get the current credentials
session = get_active_session()

Set the environment for the streamlit app

name: sf_env
channels:
- snowflake
dependencies:
- snowflake-native-apps-permission

Putting it all together

Setting your function-reference

💡 You have to add a version to the app to be able to set and use references


Adding the reference to the manifest:

references:
  - function_reference:
      label: "External function"
      description: "Function to external resource"
      privileges:
        - USAGE
      object_type: FUNCTION
      register_callback: code_schema.update_reference

Setting the reference in the streamlit app

request_attribute = "Some attribute"
# Fetching the function reference
function_reference = permission.get_reference_associations("function_reference")
if len(function_reference) == 0:
    permission.request_reference("function_reference")
else:
    results = session.sql(f"SELECT reference('function_reference')('{request_attribute}')")
		st.write("Success!")
		st.write(results)

Adding a reference setter in the setup-script

create or replace procedure code_schema.update_reference(ref_name string, operation string, ref_or_alias string)
returns string
language sql
as $$
begin
  case (operation)
    when 'ADD' then
       select system$set_reference(:ref_name, :ref_or_alias);
    when 'REMOVE' then
       select system$remove_reference(:ref_name, :ref_or_alias);
    when 'CLEAR' then
       select system$remove_all_references();
    else
       return 'Unknown operation: ' || operation;
  end case;
  return 'Success';
end;
$$;

grant usage on procedure code_schema.update_reference(string, string, string) to application role app_public;

Adding a CI-script to run as a github-action

name: CI

on:
  # Triggers the workflow on main
  push:
    branches:
      - "main"
    tags:
      - "v*"

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    env:
      SNOWSQL_PWD: ${{ secrets.SNOWFLAKE_PASSWORD }}
      SNOWSQL_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
      SNOWSQL_USER: ${{ secrets.SNOWFLAKE_USERNAME }}
      SNOWSQL_DATABASE: ${{ secrets.SNOWFLAKE_DATABASE }}
      SNOWSQL_SCHEMA: ${{ secrets.SNOWFLAKE_SCHEMA }}
      SNOWSQL_ROLE: ${{ secrets.SNOWFLAKE_ROLE }}
      SNOWSQL_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}

    steps:
      - uses: actions/checkout@v3

      - name: Install SnowSQL
        run: |
          curl -O <https://sfc-repo.snowflakecomputing.com/snowsql/bootstrap/1.2/linux_x86_64/snowsql-1.2.9-linux_x86_64.bash>
          SNOWSQL_DEST=~/bin SNOWSQL_LOGIN_SHELL=~/.profile bash snowsql-1.2.9-linux_x86_64.bash
      - name: Upload artifact to Snowflake
        env:
          GITHUB_ACTION_PATH: ${{ github.action_path }}
        run: |
          ~/bin/snowsql -q 'put file://'$(pwd)'/manifest.yml @<your-application-package>.<your-stage-schema>.<your-stage> overwrite=true auto_compress=false;'
          ~/bin/snowsql -q 'put file://'$(pwd)'/scripts/setup.sql @<your-application-package>.<your-stage-schema>.<your-stage>/scripts overwrite=true auto_compress=false;'
          ~/bin/snowsql -q 'put file://'$(pwd)'/readme.md @<your-application-package>.<your-stage-schema>.<your-stage> overwrite=true auto_compress=false;'
          ~/bin/snowsql -q 'put file://'$(pwd)'/streamlit/app.py @<your-application-package>.<your-stage-schema>.<your-stage>/streamlit overwrite=true auto_compress=false;'
          ~/bin/snowsql -q 'put file://'$(pwd)'/streamlit/environment.yml @<your-application-package>.<your-stage-schema>.<your-stage>/streamlit overwrite=true auto_compress=false;'
      - name: Create or update Application
        run: |
          ~/bin/snowsql -q 'ALTER APPLICATION PACKAGE <your_application-package> DROP VERSION V1_0;
                            ALTER APPLICATION PACKAGE <your_application-package>
                              ADD VERSION V1_0 USING '@<your-application-package>.<your-stage-schema>.<your-stage>';
                            DROP APPLICATION IF EXISTS <your-application>;
                            CREATE APPLICATION <your-application>
                              FROM APPLICATION PACKAGE <your_application-package>
                              USING VERSION V1_0;'

Now you are all done!

Feel free to reach out if you have any questions.

Comments


Stay infomed about what's new in the data business.

Sign up to our newsletter for expert insights and the latest updates in the data industry.

Address

Strandbergsgatan 61
112 51 Stockholm

Email

Follow us on

  • LinkedIn

UnionAll AB  Stockholm • 2025

bottom of page