Open62541pp provides two complementary APIs for interacting with the OPC UA address space. Understanding when to use each one avoids confusion and leads to cleaner code.
The Node API
opcua::Node is a handle to a single node identified by a opcua::NodeId. It offers a concise, object-oriented interface and throws opcua::BadStatus exceptions on failure.
auto value = node.readValue().to<int>();
auto child = node.addVariable({1, 2000}, "Counter");
High-level node class to access node attribute, browse and populate address space.
UA_Variant wrapper class.
Use the Node API when:
- writing sequential, synchronous code where exceptions are acceptable
- operating on individual nodes one at a time
- building scripts, tools, or quick integrations
The services namespace
The free functions in opcua::services mirror the OPC UA service set directly. They accept any combination of opcua::Server or opcua::Client and always return opcua::Result<T> — never throw. Asynchronous variants (suffixed Async) are also available here.
if (result) {
int v = result->to<int>();
}
client,
opcua::TimestampsToReturn::Both
);
});
The template class Result encapsulates a StatusCode and optionally a value.
UA_ReadResponse wrapper class.
ReadResponse read(Client &connection, const ReadRequest &request) noexcept
Read one or more attributes of one or more nodes (client only).
Result< Variant > readValue(T &connection, const NodeId &id) noexcept
Read the AttributeId::Value attribute of a node.
auto readValueAsync(Client &connection, const NodeId &id, CompletionToken &&token)
Read the AttributeId::Value attribute of a node.
Use the services namespace when:
- you need
Result<T> to compose or chain error handling without exceptions
- performing batched requests (multiple nodes in one round-trip)
- using callbacks, deferred execution, or custom completion tokens (full async model)
- accessing response fields not exposed by the Node API
Mixing both APIs
The two APIs are fully interchangeable. opcua::Node is a thin wrapper around the same service functions; there is no performance difference. You can obtain the opcua::NodeId from any node and pass it directly to service functions:
opcua::Node node{server, opcua::ObjectId::ObjectsFolder};
server, node.id(), {1, 1000}, "Speed", attrs,
opcua::VariableTypeId::BaseDataVariableType,
opcua::ReferenceTypeId::HasComponent
);
UA_VariableAttributes wrapper class.
Result< NodeId > addVariable(T &connection, const NodeId &parentId, const NodeId &id, std::string_view browseName, const VariableAttributes &attributes, const NodeId &variableType, const NodeId &referenceType) noexcept
Add variable.
Quick reference
| Criterion | Node API | services namespace |
| Error model | throws BadStatus | returns Result<T> |
| Async support | partial — std::future only | full — callbacks, futures, deferred, custom tokens |
| Batching | no | yes |
| Works on Server | yes | yes |
| Works on Client | yes | yes |
| Verbosity | lower | higher |