# Coding an UbiFunction

## Runtimes

UbiFunctions accept [NodeJS](https://nodejs.org/en/blog/release/v10.0.0/) or [Python](https://www.python.org/downloads/release/python-370/). You can use these languages to write the logic required to extract, transform, and analyze data.

## Entrypoint

Regardless of the chosen runtime, the UbiFunction must define a *main* function. This function serves as the entry point, meaning it is the first function called when a request is made to the UbiFunction.

## UbiFunction arguments

UbiFunctions require a JSON payload in the body of the HTTP request. This payload is passed to the `main` function as a dictionary object named `args`. The `args` object contains all parameters sent in the request, making them accessible within your function for further use.

For UbiFunctions configured with the **GET** method, the `args` object instead contains the **query parameters** passed in the URL.

Additionally, when an `X-Auth-Token` header is included in the request, its value is automatically injected into `args` as `args["token"]`, regardless of whether the request is made via `GET` or `POST`.

{% tabs %}
{% tab title="NodeJS" %}

```javascript
async function main(args) {
  var ubidots_token = args?.token;
  var device_label = args.device;
}
```

{% endtab %}

{% tab title="Python" %}

```python
def main(args):
    token = args.get('token', None)
    device = args.get('device', None)
```

{% endtab %}
{% endtabs %}

| Object       | Description                                                                                                     |
| ------------ | --------------------------------------------------------------------------------------------------------------- |
| `args`       | Any payload sent to the function will be contained in the `args` object.                                        |
| `args.token` | When an `x-auth-token` header is included in an HTTP invocation request, the `args.token` key will be included. |

### Behavior of the `args` object according to HTTP methods

{% tabs %}
{% tab title="GET" %}

```http
GET /prv/your_user/function_name?device=my_device&limit=10 HTTP/1.1
Host: parse.ubidots.com
X-Auth-Token: BBUS-abcdef0987654321

// Args within the UbiFunction
{
   "device": "my_device",
   "limit": "10",
   "token": "BBUS-abcdef0987654321"
}
```

{% endtab %}

{% tab title="POST" %}

```http
POST /prv/your_user/function_name HTTP/1.1
Host: parse.ubidots.com
Content-Type: application/json
X-Auth-Token: BBFF-1234567890abcdef

{
  "temperature": 22.5,
  "location": "lab"
}


// Args within the UbiFunction
//{
//  "temperature": 22.5,
//  "location": "lab",
//  "token": "BBFF-1234567890abcdef"
//}

```

{% endtab %}
{% endtabs %}

## Code

By default, every new UbiFunction includes sample code that uses input data (`token`, `device`, and a `variable value`) to make a request to the Ubidots API. Here are the default examples for reference:

{% tabs %}
{% tab title="NodeJS" %}

```javascript
const axios = require('axios');

// Main function - runs every time the function is executed.
// "args" is a dictionary containing both the URL params and the HTTP body (for POST requests).
async function main(args) {

  // Grab the token and device label from URL parameters, then erase them from the args dictionary
  var ubidots_token = args.token;
  var device_label = args.device;
  delete args['token'];
  delete args['device'];

  // Use the remaining parameters as payload
  var payload = args;

  // Log the payload to the console for debugging purposes. You can access the function's logs using
  // the option in the header above.
  console.log(payload);

  // Send the payload to Ubidots
  var response = await ubidots_request(ubidots_token, device_label, payload);

  // Log Ubidots response to the console
  console.log(response);

  // Pass Ubidots' API response as the function's response
  return response;
}

// This function builds an HTTP POST request to Ubidots
async function ubidots_request(token, label, body) {
  let config = {
    method: 'post',
    url: 'https://industrial.api.ubidots.com/api/v1.6/devices/' + label,
    data: body,
    headers: {
      'Content-Type': 'application/json',
      'X-Auth-Token': token
    }
  }
  const response = await axios.request(config);
  return response.data;
}

```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

BASE_URL = "https://industrial.api.ubidots.com"

REQUESTS_FUNCTIONS = {"get": requests.get, "post": requests.post}

def main(args):
    '''
    Main function - runs every time the function is executed.
    "args" is a dictionary containing both the URL params and the HTTP body (for POST requests).
    '''
    token = args.get('token', None)
    device = args.get('device', None)

    if token is None or device is None:
        print("[ERROR] Please send your Ubidots token and device label in your args")
        return {"status": "error"}
    
    del args['token']
    del args['device']

    # Log the payload to the console for debugging purposes. You can access the function's logs using
    # the option in the header above.

    print("[INFO] Payload to send: {}".format(args))

    # Use the remaining parameters as payload
    req = update_device(device, args, token)

    # Prints the request result

    print("[INFO] Request result:")
    print(req.text)

    return {"status": "Ok", "result": req.json()}

def update_device(device, payload, token):
    """
    updates a variable with a single dot
    """

    url = "{}/api/v1.6/devices/{}".format(BASE_URL, device)
    headers = {"X-Auth-Token": token, "Content-Type": "application/json"}

    req = create_request(url, headers, payload, attempts=5, request_type="post")
    
    return req

def create_request(url, headers, data, attempts, request_type):
    """
    Function to create a request to the server
    """

    request_func = REQUESTS_FUNCTIONS.get(request_type)

    kwargs = {"url": url, "headers": headers}

    if request_type == "post":
        kwargs["json"] = data

    try:
        req = request_func(**kwargs)
        print("[INFO] Request result: {}".format(req.text))
        status_code = req.status_code
        time.sleep(1)

        while status_code >= 400 and attempts < 5:
            req = request_func(**kwargs)
            print("[INFO] Request result: {}".format(req.text))
            status_code = req.status_code
            attempts += 1
            time.sleep(1)

        return req
    except Exception as e:
        print("[ERROR] There was an error with the request, details:")
        print(e)
        return None

```

{% endtab %}
{% endtabs %}

## Output Response

The output of a UbiFunction must be a JSON object. By default, the status code will be `200`, but this can be changed using [Raw Functions](/ubifunctions/advanced/raw-functions.md).

{% tabs %}
{% tab title="NodeJS" %}

```javascript
function main(args) {
    return {'temperature' : 56}
}
```

{% endtab %}

{% tab title="Python" %}

```python
def main():
    return {'temperature' : 56}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ubidots.com/ubifunctions/getting-started/coding-an-ubifunction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
