A service in Goa represents a collection of related methods that work together to provide specific functionality. Services help organize your API into logical groupings.
The Service DSL supports several options to configure and document your service:
var _ = Service("users", func() {
// Basic documentation
Description("User management service")
// Detailed documentation
Docs(func() {
Description("Detailed documentation for the user service")
URL("https://example.com/docs/users")
})
// Service-level error definitions
Error("unauthorized", String, "Authentication failed")
Error("not_found", NotFound, "Resource not found")
// Service-wide metadata
Meta("swagger:tag", "Users")
Meta("rpc:package", "usersvc")
// Security requirements
Security(OAuth2, func() {
Scope("read:users")
Scope("write:users")
})
// Service-level variables
Variable("version", String, func() {
Description("API version")
Default("v1")
Enum("v1", "v2")
})
// Methods
Method("create", func() {
// ... method definition
})
Method("list", func() {
// ... method definition
})
// Files served by the service
Files("/docs", "./swagger", func() {
Description("API documentation")
})
})
Define errors that can be returned by all methods in the service:
var _ = Service("orders", func() {
// All methods in the service will return this error
Error("unauthorized")
})
Note: The
Error
DSL is used to define errors that can be returned by all methods in the service. It is not appropriate for defining errors that are specific to a subset of methods. Instead, use theError
DSL within the method or API definition for this purpose.
Use the Docs DSL to provide detailed documentation:
var _ = Service("payments", func() {
Description("Payment processing service")
Docs(func() {
Description(`The payment service handles all payment-related operations.
It provides methods for:
- Processing payments
- Refunding transactions
- Querying payment status
- Managing payment methods`)
URL("https://example.com/docs/payments")
// Additional documentation metadata
Meta("doc:section", "Financial Services")
Meta("doc:category", "Core APIs")
})
})
Complex APIs can be organized into multiple services:
var _ = Service("users", func() {
Description("User management service")
// ... user-related methods
})
var _ = Service("billing", func() {
Description("Billing and payment service")
// ... billing-related methods
})
Methods define the operations that can be performed within a service. Each method specifies its input (payload), output (result), and error conditions.
Method("add", func() {
Description("Add two numbers together")
// Input parameters
Payload(func() {
Field(1, "a", Int32, "First operand")
Field(2, "b", Int32, "Second operand")
Required("a", "b")
})
// Success response
Result(Int32)
// Error responses
Error("overflow")
})
Methods can accept different types of payloads:
// Simple payload using existing type
Method("getUser", func() {
Payload(String, "User ID")
Result(User)
})
// Structured payload defined inline
Method("createUser", func() {
Payload(func() {
Field(1, "name", String, "User's full name")
Field(2, "email", String, "Email address", func() {
Format(FormatEmail)
})
Field(3, "role", String, "User role", func() {
Enum("admin", "user", "guest")
})
Required("name", "email", "role")
})
Result(User)
})
// Reference to predefined payload type
Method("updateUser", func() {
Payload(UpdateUserPayload)
Result(User)
})
Methods can return different types of results:
// Simple primitive result
Method("count", func() {
Result(Int64)
})
// Structured result defined inline
Method("search", func() {
Result(func() {
Field(1, "items", ArrayOf(User), "Matching users")
Field(2, "total", Int64, "Total count")
Required("items", "total")
})
})
Define expected error conditions for methods:
Method("divide", func() {
Payload(func() {
Field(1, "a", Float64, "Dividend")
Field(2, "b", Float64, "Divisor")
Required("a", "b")
})
Result(Float64)
// Method-specific errors
Error("division_by_zero", func() {
Description("Attempted to divide by zero")
})
})
Goa supports streaming for both payloads and results:
Method("streamNumbers", func() {
Description("Stream a sequence of numbers")
// Stream of integers as input
StreamingPayload(Int32)
// Stream of integers as output
StreamingResult(Int32)
})
Method("processEvents", func() {
Description("Process a stream of events")
// Stream structured data
StreamingPayload(func() {
Field(1, "event_type", String)
Field(2, "data", Any)
Required("event_type", "data")
})
// Return summary result
Result(func() {
Field(1, "processed", Int64, "Number of events processed")
Field(2, "errors", Int64, "Number of errors encountered")
Required("processed", "errors")
})
})
See the streaming tutorial for more details on streaming.
Service Organization
Method Design
Type Usage