Jump toUpdate content

Code examples for Serverless Functions

Reviewed on 01 June 2022Published on 18 October 2021

This page shows examples of functions in Python, Node, and Golang that can be used in your Serverless Functions projects, such as functions that allow you to:

  • say Hello world
  • use environment variables
  • use event components
  • use data from CRON jobs
  • 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, which enables you to access the environment variables you define in your namespaces or functions.

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 event objects

With event objects you can pass information through your HTTP request. They are 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

return {
"statusCode": 200,
}

Using data from CRON jobs in Python

CRON jobs share the same pattern as HTTP calls. You can 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

You might 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 or secret environment variable
url={YOUR URL} # if you want a dynamic url based on informations 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 variable or secret environment variable
url={YOUR URL} # if you want a dynamic url based on information sent to handle(), please define the url inside the function

def handle(event, context):
data=json.dumps({"sample_key": "sample_value"}).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 Serverless Framework (recommanded) or by using a zip-file.

Node

Returning a function result in Node

Note:

The following examples use CommonJS modules. If you use ES modules refer to the dedicated section of this documentation.

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

  • With body and statusCode. This response type sets 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",
},
}
}
  • With 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 the 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);
};
  • With 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

You might 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 sample:

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 sample

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;
};

Using ES Modules

Node has two module systems: CommonJS modules and ECMAScript (ES) modules. By default, Node treats your code files as CommonJS modules, however ES modules have also been available since the release of node16 runtime on Scaleway Serverless Functions. ES modules give you a more modern way to re-use your code.

According to the official documentation, to use ES modules you can specify the module type in package.json, as in the following example:

  ...
"type": "module",
...

This then enables you to write your code for ES modules:

export {handle};

function handle (event, context, cb) {
return {
body: process.version,
statusCode: 200,
};
};

The use of ES modules is encouraged, since they are more efficient and make setup and debugging much easier.

Note that using "type": "module" or "type": "commonjs" in your package.json will enable/disable some features in Node runtime. For a comprehensive list of differences, please refer to the official documentation, the following is a summary only:

  • commonjs is used as default value
  • commonjs allows you to use require/module.exports (synchronous code loading, it basically copies all file contents)
  • module allows you to use import/export ES6 instructions (asynchronous loading, more optimized as it imports only the pieces of code you need)

Go (>= 1.17)

Unlike old versions of Go runtime (< 1.17), there is no additional dependencies to use when writing a Go function.

But there are also requirements:

  • your code must be a valid Go module: a go.mod file is expected in the root directory
  • your handler function should be in a file at the root of your module
  • your handler must be exported, example: Handle is correct, handle is not because it is not exported
  • your handler must have the following signature: func Handle(w http.ResponseWriter, r *http.Request)
  • main package is reserved: you must not have any package named main in your module

Suggested code layout:

.
├── go.mod # your go.mod defines your module
├── go.sum # not always necessary
├── myfunc.go # your handler method (exported) must be defined here
└── subpackage # you can have subpackages
└── hello.go # with files inside

Hello world

package myfunc

import (
"encoding/json"
"fmt"
"net/http"
)

// Handle - Handle event
func Handle(w http.ResponseWriter, r *http.Request) {
response := map[string]interface{}{
"message": "We're all good",
"healthy": true,
"number": 4,
}

responseB, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(responseB))
}

Using r *http.Request object, you can get the method, the headers, the body, etc, as if you were using a simple http server.

You must use the w http.ResponseWriter parameter to send a response to the function caller.

Go (< 1.17)

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 (
"database/sql"
"fmt"
"log"
"os"

"github.com/scaleway/scaleway-functions-go/events"
"github.com/scaleway/scaleway-functions-go/lambda"
)

// Initialization logic, executed at function startup
func init() {
// 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)
}