Skip to main content

Service Node

The Service node enables easy integration with REST APIs and web services. It handles all the mechanics of creating requests, making service calls, and processing responses, letting you focus on business logic rather than HTTP plumbing.

Syntax

[synchronous] [verbose] Service [serviceName] as [alias] 
method [httpMethod] [foreach iterator]
[input inputSchema] [output outputSchema] {
Url -> @Config([baseUrlProperty])
Path -> ${[pathExpression]}
Timeout -> ${[timeoutMs]}
Retry -> ${[retryCount]}
[pathParams]
[headers]
[queryParams]
[body]
}

Parameters

Modifiers

  • synchronous (optional): Makes the service call blocking. By default, services are asynchronous
  • verbose (optional): Adds __metadata with HTTP status and response details to the output

Core Properties

  • serviceName: Identifier for the service type (use "service" for generic REST calls)
  • alias: Reference name used in flows - enables multiple calls to the same service
  • httpMethod: HTTP method (GET, POST, PUT, DELETE)
  • foreach (optional): Iterator for batch operations
  • input (optional): Input schema for request data
  • output (optional): Output schema for response filtering and validation

Configuration

  • baseUrlProperty: Configuration property containing the service base URL
  • pathExpression: Dynamic path construction using expressions
  • timeoutMs: Request timeout in milliseconds
  • retryCount: Number of retry attempts on failure

Request Parameters

  • pathParams: Dynamic path parameter substitution
  • headers: Custom HTTP headers
  • queryParams: URL query parameters
  • body: Request body for POST/PUT operations

Basic Examples

Simple GET Request

Schema User {
string id
string name
string email
}

Service userService method GET as getUserById
input UserIdRequest output User {
Url -> @Config("api.base.url")
Path -> ${"/users/" + UserIdRequest.id}
Timeout -> ${5000}
@Header Accept -> ${"application/json"}
}

POST Request with Body

Schema CreateUserRequest {
string name
string email
string department
}

Schema UserResponse {
string id
string name
string email
string status
}

Service userService method POST as createUser
input CreateUserRequest output UserResponse {
Url -> @Config("api.base.url")
Path -> ${"/users"}
Timeout -> ${10000}
Retry -> ${3}
@Header Content-Type -> ${"application/json"}
@Header Authorization -> ${"Bearer " + @Config("api.token")}
@Body -> ${CreateUserRequest}
}

Advanced Configuration

Path Parameters

Use @PathParam for dynamic URL segments:

Service orderService method GET as getOrderById
input OrderRequest output Order {
Url -> @Config("orders.api.url")
Path -> ${"/orders/{orderId}/items/{itemId}"}
@PathParam orderId -> ${OrderRequest.orderId}
@PathParam itemId -> ${OrderRequest.itemId}
@Header Authorization -> ${"Bearer " + @Config("api.token")}
}

Query Parameters

Add URL query parameters with @Query:

Service searchService method GET as searchProducts
input SearchRequest output SearchResults {
Url -> @Config("search.api.url")
Path -> ${"/search"}
@Query q -> ${SearchRequest.searchTerm}
@Query category -> ${SearchRequest.category}
@Query limit -> ${SearchRequest.maxResults}
@Query sort -> ${"relevance"}
}

Custom Headers

Add authentication and custom headers:

Service paymentService method POST as processPayment
input PaymentRequest output PaymentResponse {
Url -> @Config("payment.api.url")
Path -> ${"/payments"}
Timeout -> ${30000}
Retry -> ${2}
@Header Content-Type -> ${"application/json"}
@Header Authorization -> ${"Bearer " + @Config("payment.api.token")}
@Header Idempotency-Key -> ${uuid()}
@Header X-Request-ID -> ${PaymentRequest.requestId}
@Body -> ${PaymentRequest}
}

Service Modifiers

Synchronous Services

By default, services are asynchronous (non-blocking). Use synchronous for blocking calls:

// Flow continues immediately, service call happens in background
Service notificationService method POST as sendNotification {
Url -> @Config("notification.api.url")
Path -> ${"/notifications"}
@Body -> ${notificationData}
}

Verbose Mode

Enable verbose mode to get HTTP metadata in the response:

verbose Service diagnosticService method GET as getHealth
output HealthResponse {
Url -> @Config("service.health.url")
Path -> ${"/health"}
}

Output with verbose mode:

{
"status": "healthy",
"uptime": 12345,
"__metadata": {
"httpStatus": 200,
"responseTime": 145,
"contentType": "application/json"
}
}

Error Handling & Resilience

Retry Configuration

Service unreliableService method GET as fetchData
input DataRequest output DataResponse {
Url -> @Config("external.api.url")
Path -> ${"/data/" + DataRequest.id}
Timeout -> ${5000}
Retry -> ${3} // Retry up to 3 times
@Header Accept -> ${"application/json"}
}

Timeout Management

Service slowService method GET as getLargeDataset
output LargeDataset {
Url -> @Config("data.warehouse.url")
Path -> ${"/datasets/large"}
Timeout -> ${60000} // 60 second timeout
Retry -> ${1}
}

Real-World Examples

User Authentication Service

Schema LoginRequest {
string username
string password
string clientId
}

Schema AuthResponse {
string accessToken
string refreshToken
number expiresIn
string tokenType
}

synchronous Service authService method POST as authenticateUser
input LoginRequest output AuthResponse {
Url -> @Config("auth.service.url")
Path -> ${"/oauth/token"}
Timeout -> ${10000}
Retry -> ${2}
@Header Content-Type -> ${"application/x-www-form-urlencoded"}
@Header Authorization -> ${"Basic " + @Config("client.credentials")}
@Body -> ${
"grant_type=password" +
"&username=" + LoginRequest.username +
"&password=" + LoginRequest.password +
"&client_id=" + LoginRequest.clientId
}
}

RESTful CRUD Operations

Service userService method POST as createUser
input CreateUserRequest output UserResponse {
Url -> @Config("api.base.url")
Path -> ${"/api/v1/users"}
@Header Content-Type -> ${"application/json"}
@Header Authorization -> ${"Bearer " + @Config("api.token")}
@Body -> ${CreateUserRequest}
}

Microservice Communication

Schema OrderProcessingRequest {
string orderId
string customerId
OrderItem[] items
}

Schema ProcessingResult {
string orderId
string status
PaymentResult payment
InventoryResult inventory
ShippingResult shipping
}

// Parallel service calls for order processing
Service paymentService method POST as processPayment
input OrderProcessingRequest output PaymentResult {
Url -> @Config("payment.service.url")
Path -> ${"/payments"}
@Header Content-Type -> ${"application/json"}
@Body -> ${OrderProcessingRequest}
}

Service inventoryService method POST as checkInventory
input OrderProcessingRequest output InventoryResult {
Url -> @Config("inventory.service.url")
Path -> ${"/inventory/check"}
@Header Content-Type -> ${"application/json"}
@Body -> ${OrderProcessingRequest.items}
}

Service shippingService method POST as calculateShipping
input OrderProcessingRequest output ShippingResult {
Url -> @Config("shipping.service.url")
Path -> ${"/shipping/calculate"}
@Header Content-Type -> ${"application/json"}
@Body -> ${OrderProcessingRequest}
}

Best Practices

1. Use Configuration for URLs

Always externalize service URLs:

//  Good: Configurable URLs
Service apiService method GET as getData {
Url -> @Config("external.api.url")
Path -> ${"/data"}
}

// Avoid: Hardcoded URLs
Service apiService method GET as getData {
Url -> "https://hardcoded-api.com"
Path -> ${"/data"}
}

2. Set Appropriate Timeouts

Configure timeouts based on expected response times:

//  Good: Reasonable timeouts
Service quickLookup method GET as getReference {
Timeout -> ${2000} // Quick lookup: 2 seconds
}

Service heavyProcessing method POST as processLargeDataset {
Timeout -> ${300000} // Heavy processing: 5 minutes
}

3. Include Retry Logic

Add retries for resilience:

//  Good: Retry configuration
Service externalService method GET as fetchCriticalData {
Timeout -> ${5000}
Retry -> ${3} // Retry 3 times before failing
}

4. Use Meaningful Aliases

Choose descriptive aliases for service references:

//  Good: Descriptive aliases
Service userService method GET as getCurrentUserProfile
Service userService method PUT as updateUserPreferences

// Avoid: Generic aliases
Service userService method GET as userSvc1
Service userService method PUT as userSvc2

5. Structured Error Handling

Plan for service failures in your flows:

Flow resilientServiceFlow {
Start request {
transition {
@Config("fallback.enabled") == "true" ? primaryService : fallbackService
}
}

primaryService {
// Handle primary service response or failure
}

fallbackService {
// Fallback logic when primary service is unavailable
}
}

Service nodes make HTTP integration effortless, handling all the low-level details so you can focus on business logic and data flow.