Jump toUpdate content

Code examples for Functions as a Service

Reviewed on 18 October 2021Published on 18 October 2021

This article includes several code snippets in Python, Node, and Golang. Learn:

  • How to say Hello world
  • How to use environment variables
  • How to use event components
  • How to use fata from CRON jobs
  • How to connect to HTTP services
Requirements:

Python

Returning a function result in Python

There are several ways to return a response from a handler function:

  • Response object with HTTP information:

    def my_handler(event, context):
    return {
    "body": {
    "message": "hello world"
    },
    "statusCode": 200,
    "headers": {
    "your-header": "your-value"
    }
    }
  • Straight response without body:

    def my_handler(event, context):
    return {"message": "hello world"}
    # or
    return "my Message"
  • Stringified response body (AWS Lambda):

    import json

    def my_handler(event, context):
    return {
    "body": json.dumps({"message": "hello world"}),
    "statusCode": 200,
    }

Getting data

  • Using environment variables

    Python runtime includes the “os” library which enables you to access the environment variables you define in your Namespace or Function. The following snippet returns the environment variable “ENV” you specified in the Function or Namespace configuration

    import os
    def handle(event, context):
    env_variable = os.environ.get['ENV']
    print(env_variable)
    return {
    "body": {
    "Environment variable": env_variable
    },
    "statusCode": 200,
    }

Using the event object

With the event object you can pass information through your HTTP request. It is composed of the following objects:

  • pathParameters: map(string)string - Parameters defined in the path of the HTTP Request
  • queryStringParameters: map(string)string - Query Strings parameters of the HTTP Request
  • body: string|byte() - Body of the HTTP Request, you will have to parse it in your handler to use it properly.
  • headers: map(string)string - HTTP Request Headers
  • method: string - HTTP method used
  • isBase64Encoded: boolean - Whether the request body is base64 encoded.
import json
def handle(event, context):
QueryParam = event['queryStringParameters']['parameter']
QueryBody = json.loads(event[‘body’]) # if the body is a json
QueryBody = event['pathParameters']['parameter']
return {
"statusCode": 200,
}

Using data from CRON jobs in Python

CRON jobs share the same pattern as HTTP calls. You will find the information passed into the CRON body in the event object:

import json
def handle(event, context):
CronBody = json.loads(event[‘body’])
return {
"statusCode": 200,
}

Connecting to HTTP services in Python

Sometimes, you will need to get information or transmit data to an external service. The Scaleway python runtime includes the urllib library which enables you to connect to HTTP services without adding external libraries. Please refer to our guide on code packaging if you want to use requests.

  • GET request example

    from urllib import request, parse, error
    import os

    auth_token=os.environ['X-AUTH-TOKEN'] # you can pass token through os environment variable
    url={YOUR URL} # should you want an dynamic url based on information sent to handle(), please define the url inside the function

    def handle(event, context):
    req = request.Request(url,method="GET")
    req.add_header('X-Auth-Token',auth_token)
    try:
    res=request.urlopen(req).read().decode()
    except error.HTTPError as e:
    res=e.read().decode()

    return {
    "body": json.loads(res),
    "statusCode": 200,
    }
  • POST request example

    from urllib import request, parse, error
    import os

    auth_token=os.environ['X-AUTH-TOKEN'] # you can pass token through os environment or using a vault
    url={YOUR URL} # should you want an dynamic url based on information sent to handle(), please define the url inside the function

    def handle(event, context):
    data=json.dumps({YOUR DICT}).encode('ascii')
    req = request.Request(url, data=data, method="POST")
    req.add_header('Content-Type', 'application/json')
    req.add_header('X-Auth-Token',auth_token)

    #Sending request to Instance API
    try:
    res=request.urlopen(req).read().decode()
    except error.HTTPError as e:
    res=e.read().decode()

    return {
    "body": json.loads(res),
    "statusCode": 200,
    }
    try:
    res=request.urlopen(req).read().decode()
    except error.HTTPError as e:
    res=e.read().decode()

    return {
    "body": json.loads(res),
    "statusCode": 200,
    }
    Note:

    If you want to use other libraries like request, you need to upload your project using a zip-file.

Node

Returning a function result in Node

There are multiple ways to return a response from a handler function:

  1. return object with body and statusCode will set the status code as HTTP Response Code, and body as the Response’s body, headers as Headers.

    • Stringified body (like AWS Lambda):
    module.exports.myHandler = (event, context, callback) => {
    return {
    statusCode: 201,
    body: JSON.stringify({
    message: "async function",
    }),
    headers: {
    "Content-Type": "application/json",
    },
    }
    }
    • Not Stringified body (like AWS Lambda):
    module.exports.myHandler = (event, context, callback) => {
    return {
    statusCode: 201,
    body: {
    message: "async function",
    },
    headers: {
    "Content-Type": "application/json",
    },
    }
    }
  2. Return object/entity (number, boolean, string...)without properties body and statusCode

    module.exports.myHandler = (event, context, callback) => {

    return {
    message: "message",
    }
    // Or
    return JSON.stringify({message: "message"})
    // OR
    return "Hello, world"
    // OR
    return 1 // true,false,undefined,null...
    }

    Using callback parameter:

    module.exports.myHandler = (event, context, callback) => {
    const response = {
    statusCode: 201,
    body: {
    message: "async function",
    },
    headers: {
    "Content-Type": "application/json",
    },
    }
    // Successful response
    callback(undefined, response)
    // Error response
    callback(err)
    }
  3. Return a Promise

    module.exports.myHandler = async (event, context, callback) => {
    return {
    statusCode: 201,
    body: {
    message: "async function",
    },
    headers: {
    "Content-Type": "application/json",
    },
    }
    }

    // OR
    module.exports.myHandler = (event, context, callback) => {
    const response = {
    statusCode: 201,
    body: {
    message: "async function",
    },
    headers: {
    "Content-Type": "application/json",
    },
    }

    return new Promise((resolve, reject) => {
    // do something
    if (err) return reject(err)
    return resolve(response)
    })
    }

Using environment variables in Node

If you need to pass information to your function you can use environment variables defined at the Namespace or Function level. In JavaScript they are stored in the process.env object:

module.exports.myHandler = (event, context, callback) => {
my_parameter = process.env.MYPARAMETER

return {
statusCode: 201,
body: JSON.stringify({
parameter: my_parameter,
}),
headers: {
"Content-Type": "application/json",
},
}
}

Using event components

With the event object you can pass information through your HTTP request. It is composed of the following objects:

  • pathParameters: map(string)string - Parameters defined in the path of the HTTP Request
  • queryStringParameters: map(string)string - Query Strings parameters of the HTTP Request
  • body: string|byte() - Body of the HTTP Request, you will have to parse it in your handler to use it properly
  • headers: map(string)string - HTTP Request Headers
  • method: string - HTTP method used
  • isBase64Encoded: boolean - Whether the request body is base64 encoded
module.exports.myHandler = (event, context, callback) => {
queryStringParameters = event.queryStringParameters
body = event.body
return {
statusCode: 201,
body: JSON.stringify({
queryStringParameters: queryStringParameters,
body: body,
}),
headers: {
"Content-Type": "application/json",
},
}
}

Using data from CRON jobs in Node

CRON jobs share the same pattern as HTTP calls. You will find the information passed into the CRON body in the event object:

module.exports.myHandler = (event, context, callback) => {
body = event.body
return {
statusCode: 201,
body: JSON.stringify({
body: body,
}),
headers: {
"Content-Type": "application/json",
},
}
}

Connecting to HTTP services in Node

Sometimes, you will need to get information or transmit data to an external service. The following code snippets can be executed directly in Serverless Functions without adding external libraries. Please refer to our guide on code packaging, if you want to use fetch, axios, …

  • GET request example

    const https = require('https');

    exports.handle = async function(event, context) {
    let dataString = '';

    const response = await new Promise((resolve, reject) => {
    const req = https.get("https://pokeapi.co/api/v2/pokemon/ditto", function(res) {

    res.on('data', chunk => {
    dataString += chunk;
    });

    res.on('end', () => {
    resolve({
    statusCode: 200,
    body: JSON.stringify(JSON.parse(dataString), null, 4)
    });
    });
    });

    req.on('error', (e) => {
    reject({
    statusCode: 500,
    body: 'Something went wrong!'
    });
    });
    });

    return response;
    };
  • POST request example

    const https = require('https');
    exports.handle = async function(event, context) {
    let dataString = '';

    var postData = JSON.stringify({
    "name": "John Doe",
    "email": "jdoe@scaleway.com"
    });

    const options = {
    hostname: 'postman-echo.com',
    path: '/post',
    method: 'POST',
    headers: {
    'Content-Type': 'application/json'
    }
    };

    const response = await new Promise((resolve, reject) => {
    const req = https.request(options, res => {
    res.on('data', chunk => {
    dataString += chunk;
    });
    res.on('end', () => {
    resolve({
    statusCode: 200,
    body: JSON.stringify(JSON.parse(dataString), null, 4)
    });
    });
    res.on('error', (e) => {
    reject({
    statusCode: 500,
    body: 'Something went wrong!'
    });
    });
    });
    // write data to request body
    req.write(postData);
    req.end();
    });

    return response;
    };

Go

Our Go runtime is based on AWS lambda’s, hence you will find similarities between the two runtimes.

Hello world

Every handler must be in its package, identified by package main, and exporting a main function with the following lambda.Start statement:

package main

import (
"encoding/json"
"github.com/scaleway/scaleway-functions-go/events"
"github.com/scaleway/scaleway-functions-go/lambda"
)

func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
response := map[string]interface{}{
"message": "We're all good",
}

responseB, err := json.Marshal(response)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}

return events.APIGatewayProxyResponse{
Body: string(responseB),
StatusCode: 200,
}, nil
}

func main() {
lambda.Start(handler)
}

In order to pass data to the function response, you need to use APIGatewayProxyResponse, which contains the following parameters:

  • StatusCode: Status Code returned
  • Headers: Response Headers
  • Body: Body of the HTTP Response
  • IsBase64Encoded: boolean - Whether the response body is base64 encoded

Using environment variables in Go

If you need to pass information to your function you can use environment variables defined at the Namespace or Function level: In the following code snippet, we use environment variables to get Database configuration information

package main

import (
"fmt"
"os"
"log"
"github.com/scaleway/scaleway-functions-go/events"
"github.com/scaleway/scaleway-functions-go/lambda"
)

// Initialization logic, executed at function startup
func init() {
var err error
// For example, get Database configuration from environment variables
dbName := os.Getenv("DB_NAME")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbHost := os.Getenv("DB_HOST")
dbPort := os.Getenv("DB_PORT")

connectionString := fmt.Sprintf("%s:%s@%s:%s/%s", dbUser, dbPassword, dbHost, dbPort, dbName)
db, err = sql.Open("mysql", connectionString)
if err != nil {
log.Fatalf("error received while opening connection to Database: %v", err)
}
}
#Handler executed at each invocation
func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// Do something //...

return events.APIGatewayProxyResponse{
Body: "How to initialize stuff in a Scaleway Function",
StatusCode: 200,
}, nil
}

func main() {
lambda.Start(handler)
}

Using event components (Body, Url …)

With the APIGatewayProxyRequest structure, you can pass information through your HTTP request:

  • Resource: string - Resource path defined in API Gateway
  • Path: string - URL path for the caller
  • HTTPMethod: string - HTTP method used
  • Headers: map[string]string - HTTP Request Headers
  • QueryStringParameters: map[string]string - Query Strings parameters of the HTTP Request
  • PathParameters: map[string]string - Parameters defined in the path of the HTTP Request
  • Body: string - Body of the HTTP Request, you will have to parse it in your handler to use it properly
  • IsBase64Encoded: bool - Whether the request body is base64 encoded
package main

import (
"encoding/json"
"fmt"
"github.com/scaleway/scaleway-functions-go/events"
"github.com/scaleway/scaleway-functions-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

fmt.Printf("HTTP method = %s.\n", request.HTTPMethod)

bodySize := len(request.Body)
fmt.Printf("Body size = %d.\n", bodySize)

fmt.Println("Headers:")
for key, value := range request.Headers {
fmt.Printf(" %s: %s\n", key, value)
}

returnedMessage := "Welcome to Scaleway"
if bodySize > 0 {
returnedMessage = request.Body
}

response := map[string]interface{}{
"message": returnedMessage,
}

responseB, err := json.Marshal(response)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}

return events.APIGatewayProxyResponse{
Body: string(responseB),
StatusCode: 200,
}, nil
}

func main() {
lambda.Start(handler)
}

Using data from CRON jobs in Go

package main

import (
"encoding/json"
"github.com/scaleway/scaleway-functions-go/events"
"github.com/scaleway/scaleway-functions-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

response := map[string]interface{}{
"message": request.Body,
}

responseB, err := json.Marshal(response)
if err != nil {
return events.APIGatewayProxyResponse{}, err
}

return events.APIGatewayProxyResponse{
Body: string(responseB),
StatusCode: 201,
}, nil
}

func main() {
lambda.Start(handler)
}

Connecting to HTTP services in Go

  • GET request example

    package main

    import (
    "github.com/scaleway/scaleway-functions-go/events"
    "github.com/scaleway/scaleway-functions-go/lambda"
    "io/ioutil"
    "net/http"
    )

    func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

    req, err := http.Get("https://jsonplaceholder.typicode.com/posts")
    if err != nil {
    return events.APIGatewayProxyResponse{}, err
    }

    body, err := ioutil.ReadAll(req.Body)
    if err != nil {
    return events.APIGatewayProxyResponse{}, err
    }

    return events.APIGatewayProxyResponse{
    Body: string(body),
    StatusCode: 200,
    }, nil
    }

    func main() {
    lambda.Start(handler)
    }
  • POST request example

    package main

    import (
    "bytes"
    "encoding/json"
    "github.com/scaleway/scaleway-functions-go/events"
    "github.com/scaleway/scaleway-functions-go/lambda"
    "io/ioutil"
    "net/http"
    )

    func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

    postBody, _ := json.Marshal(map[string]string{
    "name": "example",
    "email": "example@example.com",
    })

    responseBody := bytes.NewBuffer(postBody)
    resp, err := http.Post("https://postman-echo.com/post", "application/json", responseBody)
    if err != nil {
    return events.APIGatewayProxyResponse{}, err
    }

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
    return events.APIGatewayProxyResponse{}, err
    }

    return events.APIGatewayProxyResponse{
    Body: string(body),
    StatusCode: 200,
    }, nil
    }

    func main() {
    lambda.Start(handler)
    }