Using Spanner with Cloud Run functions (1st gen)


Objectives

Write, deploy, and trigger an HTTP Cloud Run function that accesses Spanner.

Costs

This document uses Spanner and Cloud Run functions, which are billable components of Google Cloud.

Before you begin

  1. This document assumes you have a Spanner instance named test-instance and a database named example-db that uses the music application schema . For instructions on creating an instance and database with the music application schema, see Quickstart using the console or the tutorials for Getting Started in Go , Java , Node.js , or Python .

  2. Enable the Cloud Run functions and Cloud Build APIs.

    Enable the APIs

  3. Install and initialize the gcloud CLI .

    If you already have the gcloud CLI installed, update it by running the following command:

     gcloud components update 
    
  4. Prepare your development environment:

Prepare the application

  1. Clone the sample app repository to your local machine:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

  2. Change to the directory that contains the Cloud Run functions sample code for accessing Spanner:

    Node.js

    cd nodejs-docs-samples/functions/spanner/

    Python

    cd python-docs-samples/functions/spanner/

    Go

    cd golang-samples/functions/spanner/

    Java

    cd java-docs-samples/functions/spanner/
  3. Take a look at the sample code:

    Node.js

      // Imports the Google Cloud client library 
     const 
      
     { 
     Spanner 
     } 
      
     = 
      
     require 
     ( 
     ' @google-cloud/spanner 
    ' 
     ); 
     // Imports the functions framework to register your HTTP function 
     const 
      
     functions 
      
     = 
      
     require 
     ( 
     '@google-cloud/functions-framework' 
     ); 
     // Instantiates a client 
     const 
      
     spanner 
      
     = 
      
     new 
      
      Spanner 
     
     (); 
     // Your Cloud Spanner instance ID 
     const 
      
     instanceId 
      
     = 
      
     'test-instance' 
     ; 
     // Your Cloud Spanner database ID 
     const 
      
     databaseId 
      
     = 
      
     'example-db' 
     ; 
     /** 
     * HTTP Cloud Function. 
     * 
     * @param {Object} req Cloud Function request context. 
     * @param {Object} res Cloud Function response context. 
     */ 
     functions 
     . 
     http 
     ( 
     'spannerQuickstart' 
     , 
      
     async 
      
     ( 
     req 
     , 
      
     res 
     ) 
      
     = 
    >  
     { 
      
     // Gets a reference to a Cloud Spanner instance and database 
      
     const 
      
     instance 
      
     = 
      
     spanner 
     . 
     instance 
     ( 
     instanceId 
     ); 
      
     const 
      
     database 
      
     = 
      
     instance 
     . 
     database 
     ( 
     databaseId 
     ); 
      
     // The query to execute 
      
     const 
      
     query 
      
     = 
      
     { 
      
     sql 
     : 
      
     'SELECT * FROM Albums' 
     , 
      
     }; 
      
     // Execute the query 
      
     try 
      
     { 
      
     const 
      
     results 
      
     = 
      
     await 
      
     database 
     . 
     run 
     ( 
     query 
     ); 
      
     const 
      
     rows 
      
     = 
      
     results 
     [ 
     0 
     ]. 
     map 
     ( 
     row 
      
     = 
    >  
     row 
     . 
     toJSON 
     ()); 
      
     rows 
     . 
     forEach 
     ( 
     row 
      
     = 
    >  
     { 
      
     res 
     . 
     write 
     ( 
      
     `SingerId: 
     ${ 
     row 
     . 
     SingerId 
     } 
     , ` 
      
     + 
      
     `AlbumId: 
     ${ 
     row 
     . 
     AlbumId 
     } 
     , ` 
      
     + 
      
     `AlbumTitle: 
     ${ 
     row 
     . 
     AlbumTitle 
     } 
     \n` 
      
     ); 
      
     }); 
      
     res 
     . 
     status 
     ( 
     200 
     ). 
     end 
     (); 
      
     } 
      
     catch 
      
     ( 
     err 
     ) 
      
     { 
      
     res 
     . 
     status 
     ( 
     500 
     ). 
     send 
     ( 
     `Error querying Spanner: 
     ${ 
     err 
     } 
     ` 
     ); 
      
     } 
     }); 
     
    

    Python

      import 
      
     functions_framework 
     from 
      
     google.cloud 
      
     import 
      spanner 
     
     instance_id 
     = 
     "test-instance" 
     database_id 
     = 
     "example-db" 
     client 
     = 
      spanner 
     
     . 
      Client 
     
     () 
     instance 
     = 
      client 
     
     . 
      instance 
     
     ( 
     instance_id 
     ) 
     database 
     = 
     instance 
     . 
     database 
     ( 
     database_id 
     ) 
     @functions_framework 
     . 
     http 
     def 
      
     spanner_read_data 
     ( 
     request 
     ): 
     query 
     = 
     "SELECT * FROM Albums" 
     outputs 
     = 
     [] 
     with 
     database 
     . 
     snapshot 
     () 
     as 
     snapshot 
     : 
     results 
     = 
     snapshot 
     . 
     execute_sql 
     ( 
     query 
     ) 
     for 
     row 
     in 
     results 
     : 
     output 
     = 
     "SingerId: 
     {} 
     , AlbumId: 
     {} 
     , AlbumTitle: 
     {} 
     " 
     . 
     format 
     ( 
     * 
     row 
     ) 
     outputs 
     . 
     append 
     ( 
     output 
     ) 
     return 
     " 
     \n 
     " 
     . 
     join 
     ( 
     outputs 
     ) 
     
    

    Go

      // Package spanner contains an example of using Spanner from a Cloud Function. 
     package 
      
     spanner 
     import 
      
     ( 
      
     "context" 
      
     "fmt" 
      
     "log" 
      
     "net/http" 
      
     "sync" 
      
     "cloud.google.com/go/spanner" 
      
     "github.com/GoogleCloudPlatform/functions-framework-go/functions" 
      
     "google.golang.org/api/iterator" 
     ) 
     // client is a global Spanner client, to avoid initializing a new client for 
     // every request. 
     var 
      
     client 
      
     * 
     spanner 
     . 
     Client 
     var 
      
     clientOnce 
      
     sync 
     . 
     Once 
     // db is the name of the database to query. 
     var 
      
     db 
      
     = 
      
     "projects/my-project/instances/my-instance/databases/example-db" 
     func 
      
     init 
     () 
      
     { 
      
     functions 
     . 
     HTTP 
     ( 
     "HelloSpanner" 
     , 
      
     HelloSpanner 
     ) 
     } 
     // HelloSpanner is an example of querying Spanner from a Cloud Function. 
     func 
      
     HelloSpanner 
     ( 
     w 
      
     http 
     . 
     ResponseWriter 
     , 
      
     r 
      
     * 
     http 
     . 
     Request 
     ) 
      
     { 
      
     clientOnce 
     . 
     Do 
     ( 
     func 
     () 
      
     { 
      
     // Declare a separate err variable to avoid shadowing client. 
      
     var 
      
     err 
      
     error 
      
     client 
     , 
      
     err 
      
     = 
      
     spanner 
     . 
     NewClient 
     ( 
     context 
     . 
     Background 
     (), 
      
     db 
     ) 
      
     if 
      
     err 
      
     != 
      
     nil 
      
     { 
      
     http 
     . 
     Error 
     ( 
     w 
     , 
      
     "Error initializing database" 
     , 
      
     http 
     . 
     StatusInternalServerError 
     ) 
      
     log 
     . 
     Printf 
     ( 
     "spanner.NewClient: %v" 
     , 
      
     err 
     ) 
      
     return 
      
     } 
      
     }) 
      
     fmt 
     . 
     Fprintln 
     ( 
     w 
     , 
      
     "Albums:" 
     ) 
      
     stmt 
      
     := 
      
     spanner 
     . 
      Statement 
     
     { 
     SQL 
     : 
      
     `SELECT SingerId, AlbumId, AlbumTitle FROM Albums` 
     } 
      
     iter 
      
     := 
      
     client 
     . 
      Single 
     
     (). 
     Query 
     ( 
     r 
     . 
     Context 
     (), 
      
     stmt 
     ) 
      
     defer 
      
     iter 
     . 
     Stop 
     () 
      
     for 
      
     { 
      
     row 
     , 
      
     err 
      
     := 
      
     iter 
     . 
     Next 
     () 
      
     if 
      
     err 
      
     == 
      
     iterator 
     . 
     Done 
      
     { 
      
     return 
      
     } 
      
     if 
      
     err 
      
     != 
      
     nil 
      
     { 
      
     http 
     . 
     Error 
     ( 
     w 
     , 
      
     "Error querying database" 
     , 
      
     http 
     . 
     StatusInternalServerError 
     ) 
      
     log 
     . 
     Printf 
     ( 
     "iter.Next: %v" 
     , 
      
     err 
     ) 
      
     return 
      
     } 
      
     var 
      
     singerID 
     , 
      
     albumID 
      
     int64 
      
     var 
      
     albumTitle 
      
     string 
      
     if 
      
     err 
      
     := 
      
     row 
     . 
      Columns 
     
     ( 
    & singerID 
     , 
      
    & albumID 
     , 
      
    & albumTitle 
     ); 
      
     err 
      
     != 
      
     nil 
      
     { 
      
     http 
     . 
     Error 
     ( 
     w 
     , 
      
     "Error parsing database response" 
     , 
      
     http 
     . 
     StatusInternalServerError 
     ) 
      
     log 
     . 
     Printf 
     ( 
     "row.Columns: %v" 
     , 
      
     err 
     ) 
      
     return 
      
     } 
      
     fmt 
     . 
     Fprintf 
     ( 
     w 
     , 
      
     "%d %d %s\n" 
     , 
      
     singerID 
     , 
      
     albumID 
     , 
      
     albumTitle 
     ) 
      
     } 
     } 
     
    

    Java

      import 
      
     com.google.api.client.http. HttpStatusCodes 
     
     ; 
     import 
      
     com.google.cloud.functions.HttpFunction 
     ; 
     import 
      
     com.google.cloud.functions. HttpRequest 
     
     ; 
     import 
      
     com.google.cloud.functions. HttpResponse 
     
     ; 
     import 
      
     com.google.cloud.spanner. DatabaseClient 
     
     ; 
     import 
      
     com.google.cloud.spanner. DatabaseId 
     
     ; 
     import 
      
     com.google.cloud.spanner. LazySpannerInitializer 
     
     ; 
     import 
      
     com.google.cloud.spanner. ResultSet 
     
     ; 
     import 
      
     com.google.cloud.spanner. SpannerException 
     
     ; 
     import 
      
     com.google.cloud.spanner. SpannerOptions 
     
     ; 
     import 
      
     com.google.cloud.spanner. Statement 
     
     ; 
     import 
      
     com.google.common.annotations.VisibleForTesting 
     ; 
     import 
      
     com.google.common.base.MoreObjects 
     ; 
     import 
      
     java.io.PrintWriter 
     ; 
     import 
      
     java.util.logging.Level 
     ; 
     import 
      
     java.util.logging.Logger 
     ; 
     // HelloSpanner is an example of querying Spanner from a Cloud Function. 
     public 
      
     class 
     HelloSpanner 
      
     implements 
      
     HttpFunction 
      
     { 
      
     private 
      
     static 
      
     final 
      
     Logger 
      
     logger 
      
     = 
      
     Logger 
     . 
     getLogger 
     ( 
     HelloSpanner 
     . 
     class 
     . 
     getName 
     ()); 
      
     // TODO<developer>: Set these environment variables. 
      
     private 
      
     static 
      
     final 
      
     String 
      
     SPANNER_INSTANCE_ID 
      
     = 
      
     MoreObjects 
     . 
     firstNonNull 
     ( 
     System 
     . 
     getenv 
     ( 
     "SPANNER_INSTANCE" 
     ), 
      
     "my-instance" 
     ); 
      
     private 
      
     static 
      
     final 
      
     String 
      
     SPANNER_DATABASE_ID 
      
     = 
      
     MoreObjects 
     . 
     firstNonNull 
     ( 
     System 
     . 
     getenv 
     ( 
     "SPANNER_DATABASE" 
     ), 
      
     "example-db" 
     ); 
      
     private 
      
     static 
      
     final 
      
      DatabaseId 
     
      
     databaseId 
      
     = 
      
      DatabaseId 
     
     . 
     of 
     ( 
      
      SpannerOptions 
     
     . 
      getDefaultProjectId 
     
     (), 
      
     SPANNER_INSTANCE_ID 
     , 
      
     SPANNER_DATABASE_ID 
     ); 
      
     // The LazySpannerInitializer instance is shared across all instances of the HelloSpanner class. 
      
     // It will create a Spanner instance the first time one is requested, and continue to return that 
      
     // instance for all subsequent requests. 
      
     private 
      
     static 
      
     final 
      
      LazySpannerInitializer 
     
      
     SPANNER_INITIALIZER 
      
     = 
      
     new 
      
      LazySpannerInitializer 
     
     (); 
      
     @VisibleForTesting 
      
      DatabaseClient 
     
      
     getClient 
     () 
      
     throws 
      
     Throwable 
      
     { 
      
     return 
      
     SPANNER_INITIALIZER 
     . 
     get 
     (). 
     getDatabaseClient 
     ( 
     databaseId 
     ); 
      
     } 
      
     @Override 
      
     public 
      
     void 
      
     service 
     ( 
      HttpRequest 
     
      
     request 
     , 
      
      HttpResponse 
     
      
     response 
     ) 
      
     throws 
      
     Exception 
      
     { 
      
     var 
      
     writer 
      
     = 
      
     new 
      
     PrintWriter 
     ( 
     response 
     . 
     getWriter 
     ()); 
      
     try 
      
     { 
      
      DatabaseClient 
     
      
     client 
      
     = 
      
     getClient 
     (); 
      
     try 
      
     ( 
      ResultSet 
     
      
     rs 
      
     = 
      
     client 
      
     . 
      singleUse 
     
     () 
      
     . 
     executeQuery 
     ( 
      Statement 
     
     . 
     of 
     ( 
     "SELECT SingerId, AlbumId, AlbumTitle FROM Albums" 
     ))) 
      
     { 
      
     writer 
     . 
     printf 
     ( 
     "Albums:%n" 
     ); 
      
     while 
      
     ( 
     rs 
     . 
     next 
     ()) 
      
     { 
      
     writer 
     . 
     printf 
     ( 
      
     "%d %d %s%n" 
     , 
      
     rs 
     . 
     getLong 
     ( 
     "SingerId" 
     ), 
      
     rs 
     . 
     getLong 
     ( 
     "AlbumId" 
     ), 
      
     rs 
     . 
     getString 
     ( 
     "AlbumTitle" 
     )); 
      
     } 
      
     } 
      
     catch 
      
     ( 
      SpannerException 
     
      
     e 
     ) 
      
     { 
      
     writer 
     . 
     printf 
     ( 
     "Error querying database: %s%n" 
     , 
      
     e 
     . 
     getMessage 
     ()); 
      
     response 
     . 
      setStatusCode 
     
     ( 
      HttpStatusCodes 
     
     . 
     STATUS_CODE_SERVER_ERROR 
     , 
      
     e 
     . 
     getMessage 
     ()); 
      
     } 
      
     } 
      
     catch 
      
     ( 
     Throwable 
      
     t 
     ) 
      
     { 
      
     logger 
     . 
     log 
     ( 
     Level 
     . 
     SEVERE 
     , 
      
     "Spanner example failed" 
     , 
      
     t 
     ); 
      
     writer 
     . 
     printf 
     ( 
     "Error setting up Spanner: %s%n" 
     , 
      
     t 
     . 
     getMessage 
     ()); 
      
     response 
     . 
      setStatusCode 
     
     ( 
      HttpStatusCodes 
     
     . 
     STATUS_CODE_SERVER_ERROR 
     , 
      
     t 
     . 
     getMessage 
     ()); 
      
     } 
      
     } 
     } 
     
    

    The function sends a SQL query to fetch all Albums data from your database. The function executes when you make an HTTP request to the function's endpoint.

Deploy the function

To deploy the function with an HTTP trigger, run the following command in the spanner directory:

Node.js

gcloud functions deploy get \
--runtime nodejs20 --trigger-http

Use the --runtime flag to specify the runtime ID of a supported Node.js version to run your function.

Python

gcloud functions deploy spanner_read_data \
--runtime python312 --trigger-http

Use the --runtime flag to specify the runtime ID of a supported Python version to run your function.

Go

gcloud functions deploy HelloSpanner \
--runtime go121 --trigger-http

Use the --runtime flag to specify the runtime ID of a supported Go version to run your function.

Java

gcloud functions deploy java-spanner-function \
--entry-point functions.HelloSpanner \
--runtime java17 \
--memory 512MB --trigger-http

Use the --runtime flag to specify the runtime ID of a supported Java version to run your function.

Function deployment might take up to two minutes.

Note the url value returned when your function finishes deploying. You will use it when you trigger the function.

You can view your deployed functions on the Cloud Run functions page in the Google Cloud console. You can also create and edit functions on that page, and get details and diagnostics for your functions.

Trigger the function

Make an HTTP request to your function:

Node.js

curl "https:// REGION 
- PROJECT_ID 
.cloudfunctions.net/get"

Python

curl "https:// REGION 
- PROJECT_ID 
.cloudfunctions.net/spanner_read_data"

Go

curl "https:// REGION 
- PROJECT_ID 
.cloudfunctions.net/HelloSpanner"

Java

curl "https:// REGION 
- PROJECT_ID 
.cloudfunctions.net/java-spanner-function"

where REGION and PROJECT_ID match the values that are visible in your terminal when your function finishes deploying. You should see output that shows the results of the SQL query, assuming you worked through a Getting Started tutorial and populated the database:

  SingerId 
 : 
  
 2 
 , 
  
 AlbumId 
 : 
  
 2 
 , 
  
 AlbumTitle 
 : 
  
 Forever 
  
 Hold 
  
 Your 
  
 Peace 
 SingerId 
 : 
  
 1 
 , 
  
 AlbumId 
 : 
  
 2 
 , 
  
 AlbumTitle 
 : 
  
 Go 
 , 
  
 Go 
 , 
  
 Go 
 SingerId 
 : 
  
 2 
 , 
  
 AlbumId 
 : 
  
 1 
 , 
  
 AlbumTitle 
 : 
  
 Green 
 SingerId 
 : 
  
 2 
 , 
  
 AlbumId 
 : 
  
 3 
 , 
  
 AlbumTitle 
 : 
  
 Terrified 
 SingerId 
 : 
  
 1 
 , 
  
 AlbumId 
 : 
  
 1 
 , 
  
 AlbumTitle 
 : 
  
 Total 
  
 Junk 
 

You can also visit the function's URL in your browser to see the result of your SQL query.

Cleanup

To avoid incurring additional charges to your Google Cloud account for the Spanner and Cloud Run functions resources used in this document, do the following:

  1. Delete the instance:

     gcloud spanner instances delete test-instance 
    
  2. Delete the function that you deployed:

    Node.js

    gcloud functions delete get

    Python

    gcloud functions delete spanner_read_data

    Go

    gcloud functions delete HelloSpanner

    Java

    gcloud functions delete java-spanner-function

What's next

Create a Mobile Website
View Site in Mobile | Classic
Share by: