Recipe - use an integration in a canvas
Goal: GET /users returns rows from a PostgreSQL table. The connection string is a secret in app config. The same steps work for any integration: only the config and the block change. Fields are in the Integration reference and the App config reference.
1. Store the secret
Ask the person for the connection string. Never put it in an integration, a block or a chat reply.
Call save_app_config:
{
"projectId": "<project id>",
"keyName": "MAIN_DB_URL",
"value": "<postgres connection string>",
"description": "Connection string of the main database",
"isEncrypted": true,
"encodingType": "plaintext"
}2. Read the config fields
Call get_integration_schema:
{ "group": "database", "variant": "PostgreSQL" }You get config, the fields for each form, and defaults, a blank config. PostgreSQL has two forms: source: "credentials" and source: "url".
3. Save the integration
Refer to the secret as cfg: plus the key.
Call save_integration:
{
"projectId": "<project id>",
"name": "Main database",
"group": "database",
"variant": "PostgreSQL",
"config": { "source": "url", "url": "cfg:MAIN_DB_URL" }
}It answers { "id": "<integration id>" }. If the key does not exist you get App config 'MAIN_DB_URL' not found: do step 1 first.
4. Test the connection
Call test_integration_connection:
{ "projectId": "<project id>", "integrationId": "<integration id>" }Expect success: true. A warning means it connected but something will not work. If it fails, read the error, correct the app config value (save_app_config with appConfigId and a new value), and test again. There is no need to change the integration, since it reads the key when it connects.
You can also test before saving: pass group, variant and config in place of integrationId.
5. Create the route
Call save_route:
{ "projectId": "<project id>", "name": "List users", "path": "/users", "method": "GET" }6. Read the block's fields
Call get_block_schemas:
{ "blockTypes": ["db_getall"] }The contract shows every field. The database, KV and queue blocks name the integration in a field called connection. It takes the integration id.
7. Build the canvas
Call get_canvas:
{ "target": { "kind": "route", "id": "<route id>" } }Note the revision, the entrypoint id, and the id of the response block.
A block text input is a literal unless it starts with js:, and js: code must return the value. See Dynamic values and js: expressions before you fill a block field.
Call edit_canvas:
{
"target": { "kind": "route", "id": "<route id>" },
"revision": 0,
"validate": true,
"ops": [
{ "op": "add_block", "ref": "users", "type": "db_getall",
"data": {
"connection": "<integration id>",
"tableName": "users",
"conditions": [],
"columns": ["id", "name", "email"],
"sort": [{ "attribute": "id", "direction": "asc" }],
"limit": 100,
"offset": 0,
"paging": "offset"
},
"connect_from": { "from": "entrypoint_1" } },
{ "op": "connect", "from": "users", "to": "response_1" }
]
}If the canvas already has an edge from the entrypoint to the response, put { "op": "disconnect", "from": "entrypoint_1", "to": "response_1" } first.
The response body is the output of the block before it, so the caller gets the list of rows.
To filter, add a condition. A condition whose value is undefined at run time is skipped, so an optional query filter needs no if block:
"conditions": [
{ "attribute": { "kind": "column", "value": "status" }, "operator": "eq",
"value": { "kind": "literal", "value": "js:return getQueryParam('status')" }, "chain": "and" }
]8. Check, switch production on, call
Call get_system_logs:
{ "projectId": "<project id>", "level": "error", "resourceId": "<route id>" }Call save_route:
{ "routeId": "<route id>", "active": true }Production serves a promoted version only, and the version needs a production value for the integration. When the user wants it live, call commit_version, then promote_version. A person approves the promote.
Call call_route:
{ "routeId": "<route id>" }Expect status: 200 and a list of rows in body. Remember call_route runs the route for real.
Other integrations
| Block | Integration | Notes |
|---|---|---|
kv_operations | Redis, Memcached | connection, operation (get, set, delete), key. |
queue_send | Kafka, NATS, SQS, RabbitMQ, Redis | connection, destination, payload. Has a success and a failure branch. |
| A custom block | Any variant, for example OpenAI | The block takes the id through an integration_selector parameter and calls the provider in its code. |
Triggers use the id in integrationId. See Trigger reference.
Rotate a secret
Update the app config value only:
Call save_app_config:
{ "projectId": "<project id>", "appConfigId": 12, "value": "<new value>" }Test the integration again. Revoke the old credential after the new one works.
Common problems
| What you see | What to do |
|---|---|
App config 'KEY' not found | Save the app config entry before the integration. |
Fluxify API error 409: Integration already exists | Another integration has that name. Use list_integrations, or pick a new name. |
success: false on test | Host or port not reachable from the server, or wrong credentials. A database on localhost means the Fluxify server's own localhost. |
| The route answers 500 | Read the error in the call_route answer, then check get_system_logs. See Recipe, debug and fix a route or workflow. |
| The table or column is not found | tableName and columns must match the database exactly. |
| Deleting the integration | It also deletes every trigger that uses it. |
