Skip to main content

Using STRONGLY_SERVICES in Your Code

All connected services are automatically available through the STRONGLY_SERVICES environment variable. This provides a unified interface for accessing databases, AI models, workflows, and more.

Overview

When you connect services during app deployment, the platform injects a JSON object containing all connection details:

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

Service Types

Add-ons (Managed Databases)

Platform-managed database instances with automatic configuration.

Structure:

{
"addons": {
"addon-id-1": {
"type": "mongodb",
"connectionString": "mongodb://user:pass@host:27017/dbname",
"database": "mydb",
"host": "mongo-addon-id-1.svc.cluster.local",
"port": 27017,
"username": "admin",
"password": "encrypted"
},
"addon-id-2": {
"type": "postgresql",
"connectionString": "postgresql://user:pass@host:5432/dbname",
"database": "mydb",
"host": "postgres-addon-id-2.svc.cluster.local",
"port": 5432,
"username": "admin",
"password": "encrypted"
},
"addon-id-3": {
"type": "redis",
"connectionString": "redis://:password@host:6379",
"host": "redis-addon-id-3.svc.cluster.local",
"port": 6379,
"password": "encrypted"
}
}
}

Data Sources (External Databases)

External database connections with encrypted credentials.

Structure:

{
"datasources": {
"datasource-id-1": {
"type": "postgresql",
"name": "production-db",
"credentials": {
"host": "external-db.example.com",
"port": 5432,
"database": "production",
"username": "app_user",
"password": "encrypted",
"ssl": true
}
},
"datasource-id-2": {
"type": "s3",
"name": "file-storage",
"credentials": {
"region": "us-east-1",
"bucket": "my-app-files",
"accessKeyId": "AKIA...",
"secretAccessKey": "encrypted"
}
},
"datasource-id-3": {
"type": "snowflake",
"name": "analytics-warehouse",
"credentials": {
"account": "xy12345.us-east-1",
"warehouse": "COMPUTE_WH",
"database": "ANALYTICS",
"schema": "PUBLIC",
"username": "app_user",
"password": "encrypted"
}
}
}
}

AI Models (AI Gateway)

AI model endpoints with automatic authentication.

Structure:

{
"aiModels": {
"model-id-1": {
"name": "gpt-4",
"provider": "openai",
"endpoint": "https://api.openai.com/v1",
"apiKey": "sk-...",
"model": "gpt-4-turbo-preview"
},
"model-id-2": {
"name": "claude-3-opus",
"provider": "anthropic",
"endpoint": "https://api.anthropic.com/v1",
"apiKey": "sk-ant-...",
"model": "claude-3-opus-20240229"
},
"model-id-3": {
"name": "llama-2-70b",
"provider": "selfhosted",
"endpoint": "https://llama.strongly.app/v1",
"apiKey": "internal-token",
"model": "llama-2-70b-chat"
}
}
}

Workflows (REST API)

n8n workflows with REST API trigger nodes.

Structure:

{
"workflows": {
"workflow-id-1": {
"name": "data-processor",
"url": "https://workflows.strongly.app/webhook/abc123def456",
"method": "POST"
},
"workflow-id-2": {
"name": "email-sender",
"url": "https://workflows.strongly.app/webhook/xyz789uvw012",
"method": "POST"
}
}
}

Usage Examples

Node.js / Express

MongoDB Add-on

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const MongoClient = require('mongodb').MongoClient;

// Get MongoDB add-on
const mongoConfig = services.addons['addon-id-1'];

// Connect using connection string
const client = await MongoClient.connect(mongoConfig.connectionString);
const db = client.db(mongoConfig.database);

// Use the database
const users = await db.collection('users').find({ active: true }).toArray();

PostgreSQL Data Source

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const { Client } = require('pg');

// Get PostgreSQL data source
const pgConfig = services.datasources['datasource-id-1'];

// Connect to external database
const pgClient = new Client({
host: pgConfig.credentials.host,
port: pgConfig.credentials.port,
database: pgConfig.credentials.database,
user: pgConfig.credentials.username,
password: pgConfig.credentials.password,
ssl: pgConfig.credentials.ssl ? { rejectUnauthorized: false } : false
});

await pgClient.connect();

// Query data
const result = await pgClient.query('SELECT * FROM users WHERE active = $1', [true]);

AI Model (OpenAI)

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

// Get AI model configuration
const aiModel = services.aiModels['model-id-1'];

// Call OpenAI-compatible API
const response = await fetch(`${aiModel.endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${aiModel.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: aiModel.model,
messages: [
{ role: 'user', content: 'Hello, how are you?' }
]
})
});

const data = await response.json();
console.log(data.choices[0].message.content);

Workflow Trigger

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

// Get workflow configuration
const workflow = services.workflows['workflow-id-1'];

// Trigger workflow
const result = await fetch(workflow.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
input: 'process this data',
userId: 123,
timestamp: new Date().toISOString()
})
});

const response = await result.json();
console.log('Workflow result:', response);

Python / Flask

MongoDB Add-on

import os
import json
from pymongo import MongoClient

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get MongoDB add-on
mongodb_addon = services['addons']['addon-id-1']

# Connect using connection string
client = MongoClient(mongodb_addon['connectionString'])
db = client[mongodb_addon['database']]

# Use the database
users = list(db.users.find({'active': True}))

PostgreSQL Data Source

import os
import json
import psycopg2

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get PostgreSQL data source
pg_config = services['datasources']['datasource-id-1']
creds = pg_config['credentials']

# Connect to external database
conn = psycopg2.connect(
host=creds['host'],
port=creds['port'],
database=creds['database'],
user=creds['username'],
password=creds['password']
)

cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE active = %s", (True,))
users = cursor.fetchall()

AI Model (Anthropic)

import os
import json
import requests

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get AI model configuration
ai_model = services['aiModels']['model-id-2']

# Call Anthropic API
response = requests.post(
f"{ai_model['endpoint']}/messages",
headers={
'x-api-key': ai_model['apiKey'],
'anthropic-version': '2023-06-01',
'content-type': 'application/json'
},
json={
'model': ai_model['model'],
'max_tokens': 1024,
'messages': [
{'role': 'user', 'content': 'Hello, Claude!'}
]
}
)

data = response.json()
print(data['content'][0]['text'])

S3 Data Source

import os
import json
import boto3

# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))

# Get S3 data source
s3_config = services['datasources']['datasource-id-2']
creds = s3_config['credentials']

# Create S3 client
s3_client = boto3.client(
's3',
region_name=creds['region'],
aws_access_key_id=creds['accessKeyId'],
aws_secret_access_key=creds['secretAccessKey']
)

# List objects
response = s3_client.list_objects_v2(Bucket=creds['bucket'])
for obj in response.get('Contents', []):
print(obj['Key'])

React (Frontend)

For React apps, STRONGLY_SERVICES should be accessed via backend API, not directly in frontend code. However, you can inject specific values at build time:

// Backend API endpoint
const API_URL = process.env.REACT_APP_API_URL;

// Call backend which has access to STRONGLY_SERVICES
async function fetchData() {
const response = await fetch(`${API_URL}/api/data`);
return response.json();
}

Helper Functions

Service Lookup Helper

// services.js
class ServicesHelper {
constructor() {
this.services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
}

getAddon(addonId) {
return this.services.addons?.[addonId];
}

getDataSource(datasourceId) {
return this.services.datasources?.[datasourceId];
}

getAIModel(modelId) {
return this.services.aiModels?.[modelId];
}

getWorkflow(workflowId) {
return this.services.workflows?.[workflowId];
}

// Get first addon of specific type
getAddonByType(type) {
const addons = this.services.addons || {};
return Object.values(addons).find(addon => addon.type === type);
}
}

module.exports = new ServicesHelper();

Usage:

const services = require('./services');

// Get specific addon
const mongo = services.getAddon('addon-id-1');

// Get first MongoDB addon
const anyMongo = services.getAddonByType('mongodb');

Connection Pool Helper

// db.js
const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
const { MongoClient } = require('mongodb');
const { Pool } = require('pg');

class DatabaseConnections {
constructor() {
this.mongoClient = null;
this.pgPool = null;
}

async getMongo(addonId) {
if (!this.mongoClient) {
const config = services.addons[addonId];
this.mongoClient = await MongoClient.connect(config.connectionString, {
maxPoolSize: 10
});
}
return this.mongoClient.db();
}

getPgPool(datasourceId) {
if (!this.pgPool) {
const config = services.datasources[datasourceId];
this.pgPool = new Pool({
host: config.credentials.host,
port: config.credentials.port,
database: config.credentials.database,
user: config.credentials.username,
password: config.credentials.password,
max: 20,
idleTimeoutMillis: 30000
});
}
return this.pgPool;
}

async close() {
if (this.mongoClient) await this.mongoClient.close();
if (this.pgPool) await this.pgPool.end();
}
}

module.exports = new DatabaseConnections();

Error Handling

Always handle missing or invalid service configurations:

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

function getAddon(addonId) {
const addon = services.addons?.[addonId];

if (!addon) {
throw new Error(`Addon ${addonId} not found in STRONGLY_SERVICES`);
}

if (!addon.connectionString) {
throw new Error(`Addon ${addonId} missing connectionString`);
}

return addon;
}

// Usage with error handling
try {
const mongoConfig = getAddon('addon-id-1');
const client = await MongoClient.connect(mongoConfig.connectionString);
} catch (error) {
console.error('Failed to connect to addon:', error.message);
// Fallback or retry logic
}

Environment-Specific Services

Services are environment-specific (Development vs Production). The same addon ID may point to different instances:

const environment = process.env.ENVIRONMENT; // 'development' or 'production'
const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

console.log(`Running in ${environment} environment`);

// Same addon ID, different instance per environment
const mongoConfig = services.addons['primary-db'];
console.log(`Connecting to: ${mongoConfig.host}`);
// Development: mongo-dev.svc.cluster.local
// Production: mongo-prod.svc.cluster.local

Debugging

Log Service Configuration

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

// Log available services (NEVER log in production - contains secrets!)
if (process.env.NODE_ENV === 'development') {
console.log('Available addons:', Object.keys(services.addons || {}));
console.log('Available datasources:', Object.keys(services.datasources || {}));
console.log('Available AI models:', Object.keys(services.aiModels || {}));
console.log('Available workflows:', Object.keys(services.workflows || {}));
}

Validate Services on Startup

const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');

function validateServices() {
const required = {
addons: ['primary-db', 'cache'],
aiModels: ['gpt-4']
};

const missing = [];

required.addons?.forEach(id => {
if (!services.addons?.[id]) {
missing.push(`addon: ${id}`);
}
});

required.aiModels?.forEach(id => {
if (!services.aiModels?.[id]) {
missing.push(`aiModel: ${id}`);
}
});

if (missing.length > 0) {
throw new Error(`Missing required services: ${missing.join(', ')}`);
}
}

// Run on startup
validateServices();

Best Practices

  1. Parse Once: Parse STRONGLY_SERVICES once at startup, not on every request
  2. Use Connection Pools: Reuse database connections across requests
  3. Handle Errors: Always check if services exist before using
  4. Never Log Secrets: Don't log STRONGLY_SERVICES in production
  5. Validate on Startup: Check required services are available at app start
  6. Use Helpers: Create helper functions for common service access patterns

Next Steps