The API
function is a top-level DSL that defines the global properties of your service. It acts as the root of your design and establishes the foundation for all other components. Each design package can contain only one API declaration, which serves as the entry point for your service definition.
The API definition serves several important purposes:
Here’s a minimal API definition:
var _ = API("calculator", func() {
Title("Calculator API")
Description("A simple calculator service")
Version("1.0.0")
})
This creates an API named “calculator” with basic documentation. The API name should be a valid Go identifier as it’s used in generated code.
Here’s a comprehensive example showing all available API options with detailed explanations:
var _ = API("bookstore", func() {
// Basic API information - used in OpenAPI documentation
Title("Bookstore API")
Description(`A modern bookstore management API.
This API provides endpoints for:
- Managing books and inventory
- Processing orders
- Customer management
- Analytics and reporting`)
Version("2.0.0")
// Terms of service - legal requirements and usage terms
TermsOfService("https://example.com/terms")
// Contact information - who to reach for support
Contact(func() {
Name("API Support")
Email("[email protected]")
URL("https://example.com/support")
})
// License information - how the API can be used
License(func() {
Name("Apache 2.0")
URL("https://www.apache.org/licenses/LICENSE-2.0.html")
})
// Documentation - detailed guides and references
Docs(func() {
Description(`Comprehensive API documentation including:
- Getting started guides
- Authentication details
- API reference
- Best practices
- Example code`)
URL("https://example.com/docs")
})
// Server definitions - where the API can be accessed
Server("production", func() {
Description("Production server")
// Multiple hosts with variables
Host("production", func() {
Description("Production host")
// Variables in URIs are replaced at runtime
URI("https://{version}.api.example.com")
URI("grpcs://{version}.grpc.example.com")
// Define the version variable
Variable("version", String, "API version", func() {
Default("v2")
Enum("v1", "v2")
})
})
})
// Development server for testing
Server("development", func() {
Description("Development server")
Host("localhost", func() {
// Local development endpoints
URI("http://localhost:8000")
URI("grpc://localhost:8080")
})
})
})
These properties are essential for API documentation and discovery:
Title
: A short, descriptive name for your APIDescription
: A detailed explanation of what your API doesVersion
: The API version, typically following semantic versioningTermsOfService
: Link to your terms of service documentExample with markdown support:
Title("Order Management API")
Description(`
# Order Management API
This API allows you to:
- Create and manage orders
- Track shipments
- Process returns
- Generate invoices
## Rate Limits
- 1000 requests per hour for authenticated users
- 100 requests per hour for anonymous users
`)
Contact information helps API consumers reach out for support:
Contact(func() {
Name("API Team")
Email("[email protected]")
URL("https://example.com/contact")
})
This information appears in the API documentation and helps users get assistance when needed.
Specify how your API can be used:
License(func() {
Name("MIT")
URL("https://opensource.org/licenses/MIT")
})
The license information is crucial for users to understand usage rights and restrictions.
Provide additional documentation resources:
Docs(func() {
Description(`Detailed documentation including:
- API Reference
- Integration Guides
- Best Practices
- Example Code`)
URL("https://example.com/docs")
})
Servers define the endpoints where your API can be accessed. You can define multiple servers for different environments:
Server("main", func() {
Description("Main API server")
// Production host
Host("production", func() {
Description("Production endpoints")
// Support both HTTP and gRPC
URI("https://api.example.com")
URI("grpcs://grpc.example.com")
})
// Regional host with variables
Host("regional", func() {
Description("Regional endpoints")
URI("https://{region}.api.example.com")
// Define the region variable
Variable("region", String, "Geographic region", func() {
Description("AWS region for the API endpoint")
Default("us-east")
Enum("us-east", "us-west", "eu-central")
})
})
})
Variables in URIs allow for flexible configuration and can be used to:
Documentation
Versioning
Server Configuration
General Tips
The API DSL allows you to define errors at the API level that can be reused across all services and methods. This promotes consistency in error handling and reduces duplication in your design.
Defining errors at the API level establishes a consistent vocabulary and structure that can be reused throughout your services. This centralized approach ensures uniform error handling while simplifying documentation and transport mappings. Rather than defining similar errors multiple times, you can reference these shared definitions wherever needed, promoting consistency and maintainability across your entire API.
When you define an error at the API level, you establish:
Here’s a simple example, given the following API definition:
var _ = API("bookstore", func() {
Error("unauthorized", ErrorResult, "Authentication failed")
HTTP(func() {
Response("unauthorized", StatusUnauthorized)
})
GRPC(func() {
Response("unauthorized", CodeUnauthenticated)
})
})
Services and methods can reference API-level errors by name:
var _ = Service("billing", func() {
Error("unauthorized") // No need to specify the error type, description or transport mappings again
})
Services and methods can reference API-level errors by name. When they do:
This inheritance model ensures consistency while allowing flexibility in how errors are used.
If you don’t specify a type for an error, Goa uses its built-in ErrorResult
type. This type includes:
You can use this default type for simple errors or define custom types for more complex error scenarios.
API-level errors can define how they should be represented in different transport protocols. For example:
These mappings are inherited when the error is used in services and methods.
Error Organization
Error Usage
Some common patterns for API-level errors include:
By defining these common patterns at the API level, you ensure consistent error handling throughout your services.