Services
Status: [Designed] -- Area 2 of the Grove language. Syntax and semantics are defined; implementation is in progress.
Services declare contracts for external APIs. They define operations (endpoints), their inputs and outputs, and how authentication is handled. Activities reference service operations through the
fetchdata operation, making external calls type-safe and centrally managed.
Note: This page covers authenticating outbound calls from Grove activities to third-party APIs. For inbound user authentication (who is making a request to your Grove app), see Authentication Overview.
Prerequisites: Activities, Concepts Overview. What you'll learn: How to declare services, define operations, configure credentials and authentication, and reference services from activities.
Declaring a Service
A service is a top-level declaration that groups related operations for an external API:
service stripe {
credentials {
api_key: String
}
auth {
protocol: "bearer"
token: credentials.api_key
}
operation create_charge {
input: {
amount: Decimal
currency: String
customer_id: String
}
output: {
id: String
status: String
amount: Decimal
}
}
operation get_charge {
input: {
charge_id: String
}
output: {
id: String
status: String
amount: Decimal
refunded: Bool
}
}
}
Credentials
The credentials block declares what secrets the service needs at runtime. These are provided through environment configuration, not hardcoded in source:
credentials {
api_key: String
webhook_secret: String
}
Credential values are resolved at deployment time from the runtime's secret store. The Grove source only declares their names and types -- it never contains actual secret values.
Authentication
The auth block specifies how credentials are applied to outgoing requests:
auth {
protocol: "bearer"
token: credentials.api_key
}
Supported Protocols
| Protocol | Description | Fields |
|---|---|---|
"bearer" | Bearer token in the Authorization header | token |
"basic" | HTTP Basic authentication | username, password |
"api_key" | API key in a header or query parameter | key, header or query |
"oauth2" | OAuth 2.0 client credentials flow | client_id, client_secret, token_url, scope |
Bearer Token
auth {
protocol: "bearer"
token: credentials.api_key
}
Basic Authentication
auth {
protocol: "basic"
username: credentials.username
password: credentials.password
}
API Key
auth {
protocol: "api_key"
key: credentials.api_key
header: "X-API-Key"
}
OAuth 2.0
auth {
protocol: "oauth2"
client_id: credentials.client_id
client_secret: credentials.client_secret
token_url: "https://auth.example.com/token"
scope: "read write"
}
Operations
Each operation declares the shape of a request and its response:
operation create_customer {
input: {
email: String
name: String
metadata: Map<String, String>?
}
output: {
id: String
email: String
created_at: DateTime
}
}
Operations are referenced from activity fetch blocks using dot notation: service_name.operation_name.
Input and Output Types
Operation inputs and outputs can use:
- Inline object types:
{ field: Type, ... } - Named types declared in the module:
input: CreateCustomerInput - Optional fields with
? - All standard Grove types:
String,Int,Decimal,Bool,List<T>,Map<K, V>, etc.
Referencing Services from Activities
Activities call service operations through the fetch data operation:
activity create_customer v1 {
data customer {
fetch crm.create_customer {
email: input.email
name: input.name
}
}
apply(input: CustomerInput) -> CustomerResult {
return {
customer_id: customer.id
created_at: customer.created_at
}
}
}
The crm.create_customer reference resolves to the create_customer operation on the crm service. The compiler checks that the fields in the fetch block match the operation's declared input type, and the data block result conforms to the operation's output type.
Versioning
Services can optionally carry version tags:
service stripe v2 {
// updated operations
}
When a service evolves (new fields, changed response shapes), you create a new version. Activities referencing the old version continue to work until explicitly migrated.
Multiple Services in a Module
A module can declare multiple services. Each represents a distinct external system:
service stripe {
// payment operations
}
service sendgrid {
// email operations
}
service twilio {
// SMS operations
}
Activities within the module can reference any of these services.
Design Notes
Services are contracts, not implementations. The Grove source describes what data goes in and comes out. The runtime adapter maps these contracts to actual HTTP calls, gRPC invocations, or SDK methods. This separation means:
- Changing the underlying transport does not require changing Grove source.
- Service contracts can be validated at compile time.
- Mock implementations can be injected during testing.
See Also
- Activities -- Using
fetchto call service operations - Routes -- Exposing HTTP endpoints (the inbound counterpart to services)
- Webhooks -- Receiving events from external services
- Full Language Reference -- Service grammar (section 6.11)