| name | blong-adapter |
| description | Integrate Blong with external systems using adapter pattern. Supports HTTP/REST APIs, TCP protocols, SQL databases, and webhooks. Hides protocol details behind high-level APIs. Make sure to use this skill whenever the user wants to connect to a database, call an external API, integrate with an HSM or payment terminal, or wire up any external dependency — even if they just say 'add database support' or 'call this API'. |
Implementing an Adapter
Overview
Adapters integrate with external systems using the adapter design pattern. They expose high-level APIs compatible with framework conventions, independent of underlying protocols (TCP, HTTP, SQL, etc.).
Purpose
- External Integration: Communicate with databases, APIs, services, devices
- Protocol Abstraction: Hide protocol details from business logic
- High-Level API: Expose framework-compatible interfaces
- Reusability: Share adapter implementations across realms
- Testing: Mock external systems easily
Adapter Types
1. Stream-Based Adapters (TCP)
For TCP protocols with custom serialization:
- Handlers:
encode/decode for serialization
- Flow:
send → encode → TCP Stream → decode → receive → dispatch
- Examples: Payshield HSM, SMPP, ISO8583, APTRA/NDC
2. API-Based Adapters (HTTP/SDK)
For higher-level protocols:
- Flow:
send → execute → receive → dispatch
- Examples: REST APIs, SOAP, database clients
- No codec needed: JavaScript objects used directly
File Structure
adapter/
├── db.ts # Database adapter definition
├── http.ts # HTTP adapter definition
├── tcp.ts # TCP adapter definition
├── db/ # Handler group: realmname.db
│ ├── userAdd.ts
│ ├── userFind.ts
│ └── encode.ts # Stream adapter: encode handler
└── http/ # Handler group: realmname.http
├── send.ts # Transform before sending
├── receive.ts # Transform after receiving
└── ready.ts # Called when adapter ready
Built-in Adapters
1. HTTP Adapter
Use Cases:
- REST API integration
- Webhook handling
- OpenAPI/Swagger services
- JSON-RPC communication
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter(blong => ({
extends: 'adapter.http',
validation: blong.type.Object({
url: blong.type.String(),
namespace: blong.type.Optional(
blong.type.Union([blong.type.String(), blong.type.Array(blong.type.String())]),
),
imports: blong.type.Optional(blong.type.Array(blong.type.String())),
logLevel: blong.type.Optional(blong.type.String()),
}),
activation: {
default: {
url: 'https://api.example.com',
namespace: ['external'],
imports: ['codec.openapi'],
logLevel: 'info',
},
dev: {
url: 'http://localhost:8080',
logLevel: 'trace',
},
},
}));
Configuration Properties:
url: https://api.example.com
namespace: [external]
imports: [codec.openapi]
logLevel: info
tls:
ca: /path/to/ca.crt
cert: /path/to/client.crt
key: /path/to/client.key
2. TCP Adapter
Use Cases:
- Custom binary protocols
- HSM communication (Payshield, Thales)
- Legacy system integration
- High-performance protocols
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter(blong => ({
extends: 'adapter.tcp',
activation: {
default: {
host: 'hsm.example.com',
port: 1500,
namespace: ['hsm'],
imports: ['realmname.hsm'],
format: {
size: '16/integer',
},
idleSend: 10000,
maxReceiveBuffer: 4096,
},
},
}));
Configuration Properties:
host: hsm.example.com
port: 1500
listen: false
localPort: 9000
socketTimeOut: 30000
maxConnections: 10
connectionDropPolicy: oldest
format:
size: 16/integer
imports: [realmname.codec]
idleSend: 10000
idleReceive: 30000
maxReceiveBuffer: 4096
tls:
ca: /path/to/ca.crt
cert: /path/to/server.crt
key: /path/to/server.key
3. Database Adapter (Knex)
Use Cases:
- SQL database operations
- Transaction management
- Stored procedure calls
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter(blong => ({
extends: 'adapter.knex',
activation: {
default: {
namespace: ['sql'],
imports: ['realmname.db'],
client: 'pg',
connection: {
host: 'localhost',
user: 'dbuser',
password: 'dbpass',
database: 'mydb',
},
},
},
}));
4. Webhook Adapter
Use Cases:
- Receive HTTP webhooks
- Handle incoming HTTP requests
- API endpoint implementation
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter(() => ({
extends: 'adapter.webhook',
}));
5. MongoDB Adapter
Use Cases:
- NoSQL document storage
- Collection-based data management
- Schema-less data storage
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter<object>(api => ({
extends: 'adapter.mongodb',
}));
Configuration:
activation: {
default: {
kv: {
namespace: 'kv',
imports: 'release.kv',
}
}
}
6. Kubernetes Adapter
Use Cases:
- Kubernetes cluster management
- Deploy/manage resources
- Monitor cluster state
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter<object>(api => ({
extends: 'adapter.k8s',
}));
Configuration:
activation: {
default: {
k8s: {
namespace: 'k8s',
imports: 'release.k8s',
}
}
}
7. S3 Storage Adapter
Use Cases:
- Object storage operations
- File upload/download
- Cloud storage integration
Implementation:
import {adapter} from '@feasibleone/blong';
export default adapter<object>(api => ({
extends: 'adapter.s3',
}));
Configuration:
activation: {
default: {
storage: {
namespace: 'storage',
imports: 'release.storage',
}
}
}
Adapter Loop
Adapters follow a sequence of handler calls:
Stream-Based Flow
send → encode → TCP Stream → decode → receive → dispatch → loop back
API-Based Flow
send → execute → receive → dispatch → loop back
Custom Handlers
Add business-specific handlers:
import {IMeta, handler} from '@feasibleone/blong';
type Handler = ({
username: string;
email: string;
}) => Promise<{
userId: number;
}>;
export default handler(({errors}) =>
async function userAdd(
params: Parameters<Handler>[0],
$meta: IMeta
): ReturnType<Handler> {
const result = await $meta.connection.raw(
'CALL user_add(?, ?)',
[params.username, params.email]
);
if (!result.rows[0]) {
throw errors.userCreateFailed();
}
return {
userId: result.rows[0].user_id
};
}
);
Stacking Handlers
Multiple handlers with the same name can be stacked:
activation: {
default: {
http: {
imports: [
'codec.openapi',
'codec.mle',
'realmname.http'
]
}
}
}
Each handler in the stack transforms the data sequentially.
Error Handling
export default handler(
({errors}) =>
async function httpCallExternal(params, $meta) {
try {
const response = await $meta.connection.get('/api/data');
return response.data;
} catch (error) {
if (error.statusCode === 404) {
throw errors.resourceNotFound();
}
if (error.statusCode === 503) {
throw errors.serviceUnavailable();
}
throw errors.externalAPIError({cause: error});
}
},
);
Testing with Mock Adapter
import {adapter} from '@feasibleone/blong';
export default adapter(() => ({
extends: 'adapter.mock',
}));
export default handler(
() =>
async function userAdd(params) {
return {
userId: 123,
username: params.username,
};
},
);
Best Practices
- Protocol Independence: Hide protocol details from orchestrators
- Error Translation: Convert protocol errors to domain errors
- Thin Layer: Keep adapters focused on integration, not business logic
- Idempotency: Design handlers to be safely retried
- Connection Pooling: Let framework handle connection management
- Timeout Configuration: Set appropriate timeouts for external systems
- Logging: Use appropriate log levels (trace for protocol details)
- Mock for Testing: Create mock adapters for testing
- TLS Configuration: Use TLS for production communications
- One Adapter Per External System: Create separate adapters for different systems
Deployment Considerations
- Microservices: Adapters can be deployed as separate services
- Connection Management: Framework handles connection pooling and reconnection
- Scaling: Adapters scale independently based on load
- Configuration: Use environment-specific configuration for URLs, credentials
- Monitoring: Framework provides metrics for adapter performance
Examples from Codebase
- HTTP adapter:
core/test/demo/adapter/http.ts
- TCP/Payshield:
core/blong-sim-tcp/payshield/adapter/tcp.ts (declarative adapter.tcp)
- TCP simulation (listen: true):
core/blong-sim-tcp/payshield/sim/payshieldSim.ts
- Database:
core/test/db/adapter/sql.ts
- Mock adapter:
core/test/demo/adapter/mock.ts