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
Php
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 resourcesPOST
: Creates a new resourcePUT
: Updates a specific resourceDELETE
: 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:
{
"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
.
{
"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:
or
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:
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 :
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:
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:
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:
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
Testing locally:
To test locally run the following commands:
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:
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:
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:
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:
Dotnet
Prerequisites: Installation of Php, currently supported Php versions are:
Version | jellyspec runtime |
---|---|
.NET 8 | dotnet8 |
.NET 6 | dotnet6 |
Template creation:
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
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:
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:
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
Testing locally:
To test locally run the following commands:
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. |