Skip to content

Creating functions, deep dive and best practices

In this page we will take a deeper look at function creation, do's and don't and a deep dive into the jellyspec file. Functions are designed to do solve a problem, such as one of the following:

  • Process some data
  • Modify or generate something
  • Wrap a set of more complex calls to another external service
  • Analyse something and return a set of results.
  • Or whatever you can think of that can help others.

Currently functions are based on the Google Functions Framework, in the future we will support Azure Functions, AWS Lambda's and others.

CORS

CORS (Cross-Origin Resource Sharing) is a security mechanism that restricts browsers from making requests from one domain to another. This prevents malicious websites from accessing data from other websites without explicit permission. These examples show how to implement CORS to each supported languages, these should be taken and adjusted as needed.

Ruby
headers = {
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS'
}
Php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
Python
from flask import Flask, jsonify
app = Flask(**name**)

@app.route('/')
def index():
    return jsonify({'message': 'Hello, World!'})

@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
return response

if __name__ == '__main__':
app.run(debug=True)
Go
package main

import (
        "fmt"
        "net/http"
)

func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                w.Header().Set("Access-Control-Allow-Origin", "*")
                w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
                fmt.Fprintf(w, "Hello, world!")
        })

        http.ListenAndServe(":8080", nil)
}
Node
const express = require('express');
const app = express();

app.get('/', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.send('Hello, World!');
});

app.listen(3000, () => {
console.log('Server listening on port 3000');
});
Dotnet
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddDefaultPolicy(
            builder =>
            {
                builder.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
            });
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors(); 
    // ... rest of your application configuration
}

Function creation overview

Creating a function should be a straightforward process, functions by design are designed to do one thing and one thing well, and tend to me single threaded. You can use threads, however ensure you are cleaning up and do not end up with an infinite block waiting for a non returning, or long running process.

A good example would be to process 10 chunks of text at the same time, and wait for the thread group to complete before carrying on. However just iterating over a loop also works.

On the entry point to a function, it is usually a good idea to check for the correct REST verb, normally GET, POST, PUT and DELETE are used, Here are the four basic HTTP verbs used in REST commands:

  • GET: Retrieves a specific resource or a collection of resources
  • POST: Creates a new resource
  • PUT: Updates a specific resource
  • DELETE: Removes a specific resource

A function can (optionally) take an input(s) and return an output.

Passing in Data, Query Params, Body and File

Currently JellyFaaS supports handling of three types of input to functions, you specify the requirements in the jellyspec.json file. This is so the JellyFaaS SDK can validate the required parameters, generate the input class structures in all supported languages along with supplying AI (such as Gemini/ChatGPT) with the information on the function definition so it knows how to use it.

Query Params

In a URL, for example : https://example.com/doSomething?foo=bar&age=10. In this case we have a query param foo which equals bar and age which is equal to 10. These can be used in GET, POST, PUT and DELETE

Note: these are all strings internally so you will need to cast it where needed. For example, if you have lenght=10.3 then 10.3 will be a string, and you will need to cast it to a float if you wanted to use it a one.

Body

A block of JSON

Warning

You cannot use a body in a GET command, it must be used in a POST, PUT or DELETE

If you want to pass in an object (for strict typing) or you do not want to pass via query params you can use a JSON body, and example would be:

Example json for creating a person with a POST
{
  "firstName": "John",
  "lastName": "Doe",
  "age": 30,
  "email": "<john.doe@example.com>",
  "address": {
    "street": "123 Main St",
    "city": "Anytown"
  }
}

Using a JSON block you convert in code to a object, normally the object conversion process is a built in library which handles errors. This then allows you to work with the object with the correct types.

File

A Single File, such as a png, txt, csv etc.

Information

Currently JellyFaaS only supports a single file, multi file (and form-data) will be released soon.

If you want to process a file. You can still send query params too, for example resizing a photo you send the file, along with resize information in the query params width=200&height=100.

Output of Data, Body and File

You can return data from a function, again this is described in the jellyspec.json file, this allows JellyFaaS to generate output class examples in all supported languages.

Body

A block of JSON

Returning data from any REST request, GET, POST, PUT and DELETE, This is optional along with a success result, however common practices are to return the created item long with a 200.

Example json for creating a person with a `POST`
{
  "firstName": "John",
  "lastName": "Doe",
  "age": 30,
  "email": "<john.doe@example.com>",
  "address": {
    "street": "123 Main St",
    "city": "Anytown"
  }
}

If no item is returned along with a 200,404 etc:

sample when no item returned, but 200
{
    "worked":true,
    "message":"created"
}

or

sample when no item returned, but 404
{
    "worked":false,
    "message":"Cannot find user 12345"
}

Using a JSON block you convert in code to a object, normally the object conversion process is a built in library which handles errors. This then allows you to work with the object with the correct types.

File

A Single File, such as a png, txt, csv etc.

Information

Currently JellyFaaS only supports a single file, multi file (and form-data) will be released soon.

If you want to return a file.

Function Creation

JellyFaaS supports the following languages for function creation, The following sections contain information on:

  • Runtime Version Support
  • CLI command to create a working template
  • A simple HTTP request code snippet
  • Command(s) to test locally
Java

Prerequisites: Installation of Java, currently supported Java versions are:

Version jellyspec runtime
Java 11 java11
Java 17 java17
Java 21 java21

Template creation:

jellyfaas create -d <dir location> -l java -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Java Code Sample
package functions;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.BufferedWriter;
import java.io.IOException;

public class HelloWorld implements HttpFunction {

    private static final String METHOD_NOT_ALLOWED_MESSAGE = "Method Not Allowed. Only GET requests are supported.";

    @Override
    public void service(HttpRequest request, HttpResponse response) throws IOException {
        if (!request.getMethod().equals("GET")) {
        response.setStatusCode(405); // Method Not Allowed
        response.setContentType("text/plain");
        BufferedWriter writer = response.getWriter();
        writer.write(METHOD_NOT_ALLOWED_MESSAGE);
        return;
        }

        // logic for handling GET requests
        BufferedWriter writer = response.getWriter();
        writer.write("Hello World!");
    }
}

Testing locally:

To test locally run the following commands:

mvn install, if needed, then :

mvn clean package
mvn function:run

Your function is now running on http://localhost:8000. Note, its http, not http(S) locally

Php

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
Php 8.2 php82
Php 8.1 php81
Php 7.4 php74

Template creation:

jellyfaas create -d <dir location> -l php -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Php Code Sample
<?php

use Google\CloudFunctions\FunctionsFramework;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

// Register the function with Functions Framework.
FunctionsFramework::http('Example', 'example');

function example(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
    $method = $request->getMethod();

    if ($method !== 'GET') {
        $response->getBody()->write('Method Not Allowed. Only GET requests are supported.');
        return $response->withStatus(405)->withHeader('Content-Type', 'text/plain');
    }

    $greeting = "Hello, World!";

    // Return the response as plain text
    $response->getBody()->write($greeting);
    return $response->withHeader('Content-Type', 'text/plain');
}

Testing locally:

To test locally run the following commands:

For Windows
composer require google/cloud-functions-framework 
or
set "FUNCTION_TARGET=example" && php -S localhost:8080 vendor/google/cloud-functions-framework/router.php
mac/*nix run
export FUNCTION_TARGET=example
php -S localhost:8080 vendor/google/cloud-functions-framework/router.php
Python

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
Python 3.12 python312
Python 3.11 python311
Python 3.10 python310
Python 3.9 python39
Python 3.8 python38
Python 3.7 python37

Template creation:

jellyfaas create -d <dir location> -l python -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Python Code Sample
import functions_framework
from flask import jsonify, request

@functions_framework.http
def hello_world(request):
    if request.method == 'GET':
        return jsonify({'message': 'Hello, World!'})
    else:
        return jsonify({'error': 'Method Not Allowed'}), 405

Testing locally:

To test locally run the following commands:

Testing
pip install functions-framework 
functions-framework --target Example --debug
Go

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
Go 1.22 go122
Go 1.21 go121
Go 1.20 go120
Go 1.19 go119
Go 1.18 go118
Go 1.16 go116
Go 1.13 go113
Go 1.12 go112
Go 1.11 go111

Template creation:

jellyfaas create -d <dir location> -l go -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Go Code Sample
package example

import (
    "encoding/json"
    "github.com/GoogleCloudPlatform/functions-framework-go/functions"
    "net/http"
)

func init() {
    functions.HTTP("hello_world", helloWorld)
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        w.WriteHeader(http.StatusMethodNotAllowed)
        return
    }
    message := "Hello, World!"
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"message": message})
}

Testing locally:

To test locally run the following commands:

For Windows
set "FUNCTION_TARGET=Example" && set "LOCAL_ONLY=true" && go run cmd/main.go
or
set "FUNCTION_TARGET=example" && php -S localhost:8080 vendor/google/cloud-functions-framework/router.php
mac/*nix run
FUNCTION_TARGET=Example LOCAL_ONLY=true go run cmd/main.go
Ruby

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
Ruby 3.2 ruby32
Ruby 3.0 ruby30
Ruby 2.7 ruby27
Ruby 2.6 ruby26

Template creation:

jellyfaas create -d <dir location> -l php -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Ruby Code Sample
    require 'functions_framework'
    require 'json'

    FunctionsFramework.http 'hello_world' do |request|
    if request.method != 'GET'
        [405, { "Content-Type" => "application/json" }, [{ 'error' => 'Method Not Allowed' }.to_json]]
    else
        [200, { "Content-Type" => "application/json" }, [{ 'message' => 'Hello, World!' }.to_json]]
    end
    end

Testing locally:

To test locally run the following commands:

Testing
bundle install
bundle exec functions-framework-ruby --target example
Dotnet

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
.NET 8 dotnet8
.NET 6 dotnet6

Template creation:

jellyfaas create -d <dir location> -l dotnet -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Dotnet Code Sample

``` dotnet using Google.Cloud.Functions.Framework; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Text.Json; using System.Threading.Tasks;

namespace ExampleProject { public class HelloWorld : IHttpFunction { private readonly ILogger _logger;

public HelloWorld(ILogger<HelloWorld> logger)
{
_logger = logger;
}

public async Task HandleAsync(HttpContext context)
{
if (context.Request.Method != HttpMethods.Get)
{
    context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
    await context.Response.WriteAsync("Only GET requests allowed.");
    return;
}

var message = "Hello, World!";
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(message));
}

} }

```

Testing locally:

To test locally run the following commands:

Testing Command
dotnet run
Node

Prerequisites: Installation of Php, currently supported Php versions are:

Version jellyspec runtime
Node.js 20 nodejs20
Node.js 18 nodejs18
Node.js 16 nodejs16
Node.js 14 nodejs14
Node.js 12 nodejs12
Node.js 10 nodejs10
Node.js 8 nodejs8
Node.js 6 nodejs6

Template creation:

jellyfaas create -d <dir location> -l node -n <function name>

Basic HTTP Request:

This sample basic request that returns 'hello world', it also included validation that a GET verb is being used.

Node Code Sample
    const functions = require('@google-cloud/functions-framework');

    functions.http('hello_world', (req, res) => {
    if (req.method !== 'GET') {
        res.status(405).send('Method Not Allowed');
        return;
    }

    const message = 'Hello, World!';
    res.setHeader('Content-Type', 'application/json');
    res.send(message);
    });

Testing locally:

To test locally run the following commands:

For Windows, Mac/*nix
npm install @google-cloud/functions-framework
npm start

Help, things are not working

Issue Solution
Getting a 409 error Check you have the correct REST verb, for example using a GET not a POST, and if you are using a GET you do not have body.
I cannot use Tesseract Tesseract is installed on the OS Level (not as in import), and we currently do not support this, however it is on the roadmap.
CORS Support If you get issues connecting with access from UI's this is normally down to CORS', it is good practice to add cors to each function.