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
- Parse Once: Parse
STRONGLY_SERVICESonce at startup, not on every request - Use Connection Pools: Reuse database connections across requests
- Handle Errors: Always check if services exist before using
- Never Log Secrets: Don't log
STRONGLY_SERVICESin production - Validate on Startup: Check required services are available at app start
- Use Helpers: Create helper functions for common service access patterns