Referencia DSL

Complete reference for Goa’s design language - data modeling, services, methods, HTTP/gRPC mapping, and security.

El Lenguaje Específico de Dominio (DSL) de Goa es la piedra angular del enfoque de diseño primero. Esta referencia cubre todos los aspectos del DSL, desde las definiciones básicas de tipos hasta las complejas asignaciones de transporte y los esquemas de seguridad.

Modelado de datos

Goa proporciona un potente sistema de tipos para modelar su dominio con precisión. Desde simples primitivas a complejas estructuras anidadas, el DSL ofrece una forma natural de expresar relaciones de datos, restricciones y reglas de validación.

Tipos primitivos

Goa proporciona estos tipos primitivos incorporados:

Boolean  // JSON boolean
Int      // Signed integer
Int32    // Signed 32-bit integer 
Int64    // Signed 64-bit integer
UInt     // Unsigned integer
UInt32   // Unsigned 32-bit integer
UInt64   // Unsigned 64-bit integer
Float32  // 32-bit floating number
Float64  // 64-bit floating number
String   // JSON string
Bytes    // Binary data
Any      // Arbitrary JSON value

Definición del tipo

La función Type DSL es la forma principal de definir tipos de datos estructurados:

var Person = Type("Person", func() {
    Description("A person")
    
    // Basic attribute
    Attribute("name", String)
    
    // Attribute with validation
    Attribute("age", Int32, func() {
        Minimum(0)
        Maximum(120)
    })
    
    // Required fields
    Required("name", "age")
})

Tipos complejos

Matrices

Las matrices definen colecciones ordenadas con validación opcional:

var Names = ArrayOf(String, func() {
    MinLength(1)
    MaxLength(10)
})

var Team = Type("Team", func() {
    Attribute("members", ArrayOf(Person))
})

Mapas

Los mapas proporcionan asociaciones clave-valor con seguridad de tipo:

var Config = MapOf(String, Int32, func() {
    Key(func() {
        Pattern("^[a-z]+$")
    })
    Elem(func() {
        Minimum(0)
    })
})

Composición de tipos

Referencia

Utilice Reference para heredar definiciones de atributos de otro tipo:

var Employee = Type("Employee", func() {
    Reference(Person)
    Attribute("name")  // Inherits from Person
    Attribute("age")   // Inherits from Person
    
    Attribute("employeeID", String, func() {
        Format(FormatUUID)
    })
})

Ampliar

Extend crea un nuevo tipo que hereda automáticamente todos los atributos:

var Manager = Type("Manager", func() {
    Extend(Employee)
    Attribute("reports", ArrayOf(Employee))
})

Reglas de validación

Validaciones de cadena

  • Pattern(regex) - Valida frente a una expresión regular
  • MinLength(n) - Longitud mínima de cadena
  • MaxLength(n) - Longitud máxima de cadena
  • Format(format) - Validación con formatos predefinidos

Validaciones numéricas

  • Minimum(n) - Valor mínimo (inclusive)
  • Maximum(n) - Valor máximo (inclusive)
  • ExclusiveMinimum(n) - Valor mínimo (excluyente)
  • ExclusiveMaximum(n) - Valor máximo (excluyente)

Validaciones de la colección

  • MinLength(n) - Número mínimo de elementos
  • MaxLength(n) - Número máximo de elementos

Validaciones de objetos

  • Required("field1", "field2") - Campos obligatorios

Validaciones genéricas

  • Enum(value1, value2) - Restringe a valores enumerados

Ejemplo combinado:

var UserProfile = Type("UserProfile", func() {
    Attribute("username", String, func() {
        Pattern("^[a-z0-9]+$")
        MinLength(3)
        MaxLength(50)
    })
    
    Attribute("email", String, func() {
        Format(FormatEmail)
    })
    
    Attribute("age", Int32, func() {
        Minimum(18)
        ExclusiveMaximum(150)
    })
    
    Attribute("tags", ArrayOf(String, func() { 
        Enum("tag1", "tag2", "tag3") 
    }), func() {
        MinLength(1)
        MaxLength(10)
    })

    Required("username", "email", "age")
})

Formatos incorporados

Goa incluye formatos predefinidos para patrones de datos comunes:

FormatoDescripción
FormatDate Valores de fecha RFC3339
FormatDateTime Valores de fecha y hora RFC3339
FormatUUID Valores UUID RFC4122
FormatEmail Direcciones de correo electrónico RFC5322
RFC1035 Nombres de host de Internet
RFC2373 Valores de dirección IPv4
FormatIPv6 Valores de dirección IPv6 RFC2373
FormatIPValores de dirección IPv4 o IPv6 RFC2373
FormatURIValores de URI de RFC3986
FormatMAC Direcciones IEEE 802 MAC-48/EUI-48/EUI-64
FormatCIDR Notación CIDR RFC4632/RFC4291
FormatRegexp Sintaxis de expresiones regulares RE2
FormatJSON Texto JSON
FormatRFC1123 Valores fecha-hora RFC1123

Atributo vs Campo DSL

Utilice Attribute para tipos sólo HTTP. Utilice Field cuando necesite soporte gRPC (incluye etiqueta de número de campo):

// HTTP only
var Person = Type("Person", func() {
    Attribute("name", String)
    Attribute("age", Int32)
})

// With gRPC support
var Person = Type("Person", func() {
    Field(1, "name", String)
    Field(2, "age", Int32)
})

Ejemplos

Proporcionar valores de ejemplo para la documentación:

var User = Type("User", func() {
    Attribute("name", String, func() {
        Example("John Doe")
    })
    
    Attribute("email", String, func() {
        Example("work", "[email protected]")
        Example("personal", "[email protected]")
        Format(FormatEmail)
    })
})

var Address = Type("Address", func() {
    Attribute("street", String)
    Attribute("city", String)
    Required("street", "city")
    
    Example("Home Address", func() {
        Description("Example of a residential address")
        Value(Val{
            "street": "123 Main St",
            "city": "Boston",
        })
    })
})

Definición de la API

La función API define propiedades globales para su servicio y sirve como raíz de su diseño.

Estructura Básica

var _ = API("calculator", func() {
    Title("Calculator API")
    Description("A simple calculator service")
    Version("1.0.0")
})

Ejemplo completo

var _ = API("bookstore", func() {
    Title("Bookstore API")
    Description("A modern bookstore management API")
    Version("2.0.0")
    TermsOfService("https://example.com/terms")
    
    Contact(func() {
        Name("API Support")
        Email("[email protected]")
        URL("https://example.com/support")
    })
    
    License(func() {
        Name("Apache 2.0")
        URL("https://www.apache.org/licenses/LICENSE-2.0.html")
    })
    
    Docs(func() {
        Description("Comprehensive API documentation")
        URL("https://example.com/docs")
    })
})

Configuración del servidor

Defina dónde se puede acceder a su API:

var _ = API("bookstore", func() {
    Server("production", func() {
        Description("Production server")
        
        Host("production", func() {
            URI("https://{version}.api.example.com")
            URI("grpcs://{version}.grpc.example.com")
            
            Variable("version", String, "API version", func() {
                Default("v2")
                Enum("v1", "v2")
            })
        })
    })
    
    Server("development", func() {
        Host("localhost", func() {
            URI("http://localhost:8000")
            URI("grpc://localhost:8080")
        })
    })
})

Errores a nivel de API

Define errores reutilizables a nivel de API:

var _ = API("bookstore", func() {
    Error("unauthorized", ErrorResult, "Authentication failed")
    
    HTTP(func() {
        Response("unauthorized", StatusUnauthorized)
    })
    
    GRPC(func() {
        Response("unauthorized", CodeUnauthenticated)
    })
})

Los servicios pueden hacer referencia a estos errores por su nombre:

var _ = Service("billing", func() {
    Error("unauthorized")  // Inherits all properties
})

Servicios y métodos

Los servicios agrupan métodos relacionados que proporcionan una funcionalidad específica.

Servicio DSL

var _ = Service("users", func() {
    Description("User management service")
    
    Docs(func() {
        Description("Detailed documentation for the user service")
        URL("https://example.com/docs/users")
    })

    // Service-level errors
    Error("unauthorized", String, "Authentication failed")
    Error("not_found", NotFound, "Resource not found")
    
    // Metadata
    Meta("swagger:tag", "Users")
    
    // Security requirements
    Security(OAuth2, func() {
        Scope("read:users")
        Scope("write:users")
    })
    
    Method("create", func() {
        // ... method definition
    })
    
    Method("list", func() {
        // ... method definition
    })
})

Método DSL

Method("add", func() {
    Description("Add two numbers together")
    
    Payload(func() {
        Field(1, "a", Int32, "First operand")
        Field(2, "b", Int32, "Second operand")
        Required("a", "b")
    })
    
    Result(Int32)
    
    Error("overflow")
})

Tipos de carga útil

// Simple payload
Method("getUser", func() {
    Payload(String, "User ID")
    Result(User)
})

// Structured payload
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 type
Method("updateUser", func() {
    Payload(UpdateUserPayload)
    Result(User)
})

Tipos de resultado

// Simple result
Method("count", func() {
    Result(Int64)
})

// Structured result
Method("search", func() {
    Result(func() {
        Field(1, "items", ArrayOf(User), "Matching users")
        Field(2, "total", Int64, "Total count")
        Required("items", "total")
    })
})

Métodos de transmisión

Method("streamNumbers", func() {
    Description("Stream a sequence of numbers")
    StreamingPayload(Int32)
    StreamingResult(Int32)
})

Method("processEvents", func() {
    StreamingPayload(func() {
        Field(1, "event_type", String)
        Field(2, "data", Any)
        Required("event_type", "data")
    })
    
    Result(func() {
        Field(1, "processed", Int64)
        Field(2, "errors", Int64)
        Required("processed", "errors")
    })
})

Streaming

El streaming permite el intercambio continuo de datos en tiempo real entre clientes y servidores. El DSL de streaming de Goa es independiente del transporte - el mismo diseño funciona tanto para HTTP (WebSocket, Server-Sent Events) como para streaming gRPC.

Patrones de transmisión

Goa soporta tres patrones de streaming:

PatrónDSLCaso de Uso
Servidor a cliente StreamingResult Transmisión en directo, notificaciones, actualizaciones de progreso
Cliente-servidor StreamingPayload Carga de archivos, ingesta de eventos..
Chat, colaboración en tiempo real

StreamingPayload

Utilice StreamingPayload cuando los clientes envían un flujo de mensajes al servidor:

// Client streams events, server returns summary
Method("ingestEvents", func() {
    Description("Ingest a stream of analytics events")
    
    StreamingPayload(func() {
        Field(1, "event_type", String, "Type of event")
        Field(2, "timestamp", String, "ISO 8601 timestamp")
        Field(3, "properties", MapOf(String, Any), "Event properties")
        Required("event_type", "timestamp")
    })
    
    Result(func() {
        Field(1, "events_processed", Int64, "Total events ingested")
        Field(2, "errors", Int64, "Events that failed validation")
        Required("events_processed", "errors")
    })
})

StreamingResultado

Utilice StreamingResult cuando el servidor envía un flujo de mensajes al cliente:

// Client subscribes, server streams updates
Method("subscribe", func() {
    Description("Subscribe to real-time price updates")
    
    Payload(func() {
        Field(1, "symbols", ArrayOf(String), "Stock symbols to watch")
        Required("symbols")
    })
    
    StreamingResult(func() {
        Field(1, "symbol", String, "Stock symbol")
        Field(2, "price", Float64, "Current price")
        Field(3, "change", Float64, "Price change percentage")
        Field(4, "timestamp", String, "Update timestamp")
        Required("symbol", "price", "timestamp")
    })
})

Streaming bidireccional

Combina ambos para una comunicación full-duplex:

// Real-time chat with structured messages
Method("chat", func() {
    Description("Bidirectional chat stream")
    
    StreamingPayload(func() {
        Field(1, "message", String, "Chat message content")
        Field(2, "room_id", String, "Target chat room")
        Field(3, "reply_to", String, "Message ID being replied to")
        Required("message", "room_id")
    })
    
    StreamingResult(func() {
        Field(1, "id", String, "Message ID")
        Field(2, "sender", String, "Sender username")
        Field(3, "message", String, "Message content")
        Field(4, "room_id", String, "Chat room")
        Field(5, "timestamp", String, "When message was sent")
        Required("id", "sender", "message", "room_id", "timestamp")
    })
})

Asignación de transporte

El mismo diseño de streaming se asigna a diferentes transportes:

Method("watch", func() {
    StreamingResult(Event)
    
    // HTTP: WebSocket by default, or SSE with ServerSentEvents()
    HTTP(func() {
        GET("/events/watch")
        // ServerSentEvents()  // Uncomment for SSE instead of WebSocket
    })
    
    // gRPC: Server-side streaming RPC
    GRPC(func() {
        Response(CodeOK)
    })
})

Véase también:


Archivos estáticos

El DSL Files sirve contenido estático directamente desde el sistema de ficheros. Esta es una característica sólo HTTP - gRPC no soporta el servicio de archivos estáticos.

Uso Básico

var _ = Service("web", func() {
    // Serve a directory
    Files("/static/{*path}", "./public/static")
    
    // Serve a specific file
    Files("/favicon.ico", "./public/favicon.ico")
    
    // Serve index.html for root
    Files("/", "./public/index.html")
})

Servicio de directorios

El comodín {*path} captura la ruta URL restante:

// Request: GET /assets/css/style.css
// Serves: ./static/css/style.css
Files("/assets/{*path}", "./static")

Aplicaciones de una sola página

Para SPAs con enrutamiento del lado del cliente, sirva el mismo HTML para todas las rutas:

var _ = Service("spa", func() {
    // API endpoints
    Method("getData", func() {
        HTTP(func() { GET("/api/data") })
    })
    
    // Static assets
    Files("/assets/{*path}", "./dist/assets")
    
    // SPA catch-all — serves index.html for all other routes
    Files("/{*path}", "./dist/index.html")
})

Nota: Files es sólo HTTP. Para obtener patrones detallados, incluida la integración de plantillas, consulte Guía HTTP: Contenido estático.


Tratamiento de errores (nivel de diseño)

Los errores en Goa se definen a nivel de diseño y se asignan automáticamente a respuestas específicas del transporte. Esta sección cubre el DSL para definir errores; para detalles de mapeo de transporte, ver Guía de Manejo de Errores.

Ámbitos de error

Los errores se pueden definir a tres niveles:

ÁmbitoDisponibilidadCaso de Uso
Nivel de API Todos los servicios Errores comunes (no autorizado, tasa limitada)
Nivel de servicio Todos los métodos del servicio Errores de dominio (no encontrado, estado no válido)
Nivel de método: Sólo un método. Errores específicos de la operación

Errores a nivel de API

Definir una vez, utilizar en todas partes:

var _ = API("myapi", func() {
    // Define common errors
    Error("unauthorized", ErrorResult, "Authentication required")
    Error("rate_limited", ErrorResult, "Too many requests", func() {
        Temporary()  // Client should retry
    })
    
    // Map to transports
    HTTP(func() {
        Response("unauthorized", StatusUnauthorized)
        Response("rate_limited", StatusTooManyRequests)
    })
    
    GRPC(func() {
        Response("unauthorized", CodeUnauthenticated)
        Response("rate_limited", CodeResourceExhausted)
    })
})

Errores a nivel de servicio

Disponible para todos los métodos del servicio:

var _ = Service("users", func() {
    // Service-wide errors
    Error("not_found", ErrorResult, "User not found")
    Error("already_exists", ErrorResult, "User already exists")
    
    HTTP(func() {
        Response("not_found", StatusNotFound)
        Response("already_exists", StatusConflict)
    })
    
    Method("get", func() {
        // Can return not_found without declaring it
    })
    
    Method("create", func() {
        // Can return already_exists without declaring it
    })
})

Errores a nivel de método

Específicos de una sola operación:

Method("transfer", func() {
    Description("Transfer funds between accounts")
    
    Payload(func() {
        Field(1, "from_account", String)
        Field(2, "to_account", String)
        Field(3, "amount", Float64)
        Required("from_account", "to_account", "amount")
    })
    
    Result(TransferResult)
    
    // Method-specific errors
    Error("insufficient_funds", ErrorResult, "Account has insufficient balance")
    Error("account_locked", ErrorResult, "Account is locked for transfers")
    
    HTTP(func() {
        POST("/transfer")
        Response("insufficient_funds", StatusUnprocessableEntity)
        Response("account_locked", StatusForbidden)
    })
})

Propiedades de error

Marcar errores con propiedades semánticas:

Error("service_unavailable", ErrorResult, func() {
    Description("Backend service is temporarily unavailable")
    Temporary()  // Client should retry
})

Error("request_timeout", ErrorResult, func() {
    Description("Request exceeded time limit")
    Timeout()    // Deadline was exceeded
})

Error("internal_error", ErrorResult, func() {
    Description("Unexpected server error")
    Fault()      // Server-side issue
})

Tipos de error personalizados

Para errores que necesitan contexto adicional:

var ValidationError = Type("ValidationError", func() {
    Field(1, "name", String, "Error name", func() {
        Meta("struct:error:name")  // Required for custom error types
    })
    Field(2, "message", String, "Error message")
    Field(3, "field", String, "Field that failed validation")
    Field(4, "value", Any, "Invalid value provided")
    Required("name", "message", "field")
})

Method("create", func() {
    Error("validation_error", ValidationError, "Input validation failed")
})

Véase también:


Asignación de transporte HTTP

El DSL HTTP define cómo se asignan los métodos de servicio a los puntos finales HTTP.

Componentes de solicitud HTTP

Una petición HTTP tiene cuatro partes que pueden ser asignadas a atributos de carga útil:

  1. URL Path Parameters - por ejemplo, /bottle/{id}
  2. **Parámetros de cadena de consulta
  3. **Cabeceras HTTP
  4. **Cuerpo de la solicitud

Expresiones de asignación:

  • Param - Carga desde la ruta o cadena de consulta
  • Header - Se carga desde las cabeceras HTTP
  • Body - Se carga desde el cuerpo de la solicitud

Asignación de cargas útiles que no son objetos

Para cargas primitivas, matrices o mapas, los valores se cargan desde el primer elemento definido:

// Path parameter
Method("show", func() {
    Payload(Int)
    HTTP(func() {
        GET("/{id}")
    })
})
// GET /1 → Show(1)

// Array in path (comma-separated)
Method("delete", func() {
    Payload(ArrayOf(String))
    HTTP(func() {
        DELETE("/{ids}")
    })
})
// DELETE /a,b → Delete([]string{"a", "b"})

// Array in query string
Method("list", func() {
    Payload(ArrayOf(String))
    HTTP(func() {
        GET("")
        Param("filter")
    })
})
// GET /?filter=a&filter=b → List([]string{"a", "b"})

// Header
Method("list", func() {
    Payload(Float32)
    HTTP(func() {
        GET("")
        Header("version")
    })
})
// GET / with header version=1.0 → List(1.0)

// Map in body
Method("create", func() {
    Payload(MapOf(String, Int))
    HTTP(func() {
        POST("")
    })
})
// POST / {"a": 1, "b": 2} → Create(map[string]int{"a": 1, "b": 2})

Asignación de cargas útiles de objetos

Para las cargas útiles de objetos, especifique de dónde procede cada atributo:

Method("create", func() {
    Payload(func() {
        Attribute("id", Int)
        Attribute("name", String)
        Attribute("age", Int)
    })
    HTTP(func() {
        POST("/{id}")
        // id from path, name and age from body
    })
})
// POST /1 {"name": "a", "age": 2} → Create(&CreatePayload{ID: 1, Name: "a", Age: 2})

Utilice Body para especificar un cuerpo que no sea objeto:

Method("rate", func() {
    Payload(func() {
        Attribute("id", Int)
        Attribute("rates", MapOf(String, Float64))
    })
    HTTP(func() {
        PUT("/{id}")
        Body("rates")  // Body is the map directly
    })
})
// PUT /1 {"a": 0.5, "b": 1.0} → Rate(&RatePayload{ID: 1, Rates: map[string]float64{...}})

Asignación de nombres de elementos

Mapea nombres de elementos HTTP a nombres de atributos:

Header("version:X-Api-Version")  // version attribute from X-Api-Version header

Body(func() {
    Attribute("name:n")  // name attribute from "n" field in JSON
    Attribute("age:a")   // age attribute from "a" field in JSON
})

Mapeo de transporte gRPC

El DSL gRPC define cómo los métodos de servicio se asignan a los procedimientos gRPC.

Características de gRPC

  1. Message Mapping - Definir estructuras de petición/respuesta con números de campo
  2. Códigos de estado - Asignar resultados a códigos de estado gRPC
  3. Metadatos - Configurar el manejo de metadatos gRPC

Soporte de Protocolos Mixtos

Los servicios pueden soportar tanto HTTP como gRPC:

Method("create", func() {
    Payload(CreatePayload)
    Result(User)
    
    HTTP(func() {
        POST("/")
        Response(StatusCreated)
    })
    
    GRPC(func() {
        Response(CodeOK)
    })
})

Mapeo de recursos RESTful:

Service("users", func() {
    HTTP(func() {
        Path("/users")
    })
    
    Method("list", func() {
        HTTP(func() { GET("/") })           // GET /users
        GRPC(func() { Response(CodeOK) })
    })
    
    Method("show", func() {
        HTTP(func() { GET("/{id}") })       // GET /users/{id}
        GRPC(func() { Response(CodeOK) })
    })
})

Seguridad

Goa proporciona construcciones DSL para autenticación y autorización.

Esquemas de seguridad

JWT (Token Web JSON)

var JWTAuth = JWTSecurity("jwt", func() {
    Description("JWT-based authentication")
    Scope("api:read", "Read-only access")
    Scope("api:write", "Read and write access")
})

Claves API

var APIKeyAuth = APIKeySecurity("api_key", func() {
    Description("API key-based authorization")
})

Autenticación básica

var BasicAuth = BasicAuthSecurity("basic", func() {
    Description("Username/password authentication")
    Scope("api:read", "Read-only access")
})

OAuth2

var OAuth2Auth = OAuth2Security("oauth2", func() {
    AuthorizationCodeFlow(
        "http://auth.example.com/authorize",
        "http://auth.example.com/token",
        "http://auth.example.com/refresh",
    )
    Scope("api:read", "Read-only access")
    Scope("api:write", "Read and write access")
})

Aplicación de la seguridad

Nivel de método

Method("secure_endpoint", func() {
    Security(JWTAuth, func() {
        Scope("api:read")
    })
    
    Payload(func() {
        TokenField(1, "token", String)
        Required("token")
    })
    
    HTTP(func() {
        GET("/secure")
        Header("token:Authorization")
    })
})

Esquemas múltiples

Method("doubly_secure", func() {
    Security(JWTAuth, APIKeyAuth, func() {
        Scope("api:write")
    })
    
    Payload(func() {
        TokenField(1, "token", String)
        APIKeyField(2, "api_key", "key", String)
        Required("token", "key")
    })
    
    HTTP(func() {
        POST("/secure")
        Param("key:k")
    })
})

Implementación de la seguridad

Goa genera una interfaz Auther que tu servicio debe implementar:

type Auther interface {
    BasicAuth(ctx context.Context, user, pass string, scheme *security.BasicScheme) (context.Context, error)
    JWTAuth(ctx context.Context, token string, scheme *security.JWTScheme) (context.Context, error)
    APIKeyAuth(ctx context.Context, key string, scheme *security.APIKeyScheme) (context.Context, error)
    OAuth2Auth(ctx context.Context, token string, scheme *security.OAuth2Scheme) (context.Context, error)
}

Ejemplo de implementación de JWT:

func (s *svc) JWTAuth(ctx context.Context, token string, scheme *security.JWTScheme) (context.Context, error) {
    claims := make(jwt.MapClaims)
    
    _, err := jwt.ParseWithClaims(token, claims, func(_ *jwt.Token) (interface{}, error) { 
        return Key, nil 
    })
    if err != nil {
        return ctx, ErrInvalidToken
    }

    // Validate required scopes
    scopes, ok := claims["scopes"].([]any)
    if !ok {
        return ctx, ErrInvalidTokenScopes
    }
    scopesInToken := make([]string, len(scopes))
    for _, scp := range scopes {
        scopesInToken = append(scopesInToken, scp.(string))
    }
    if err := scheme.Validate(scopesInToken); err != nil {
        return ctx, ErrInvalidScopes
    }

    return contextWithAuthInfo(ctx, authInfo{claims: claims}), nil
}

Ejemplo de implementación de Basic Auth:

func (s *svc) BasicAuth(ctx context.Context, user, pass string, scheme *security.BasicScheme) (context.Context, error) {
    if user != "goa" || pass != "rocks" {
        return ctx, ErrUnauthorized
    }
    return contextWithAuthInfo(ctx, authInfo{user: user}), nil
}

Mejores prácticas

Organización de tipos

  • Agrupar tipos relacionados
  • Utilizar nombres de campo y descripciones significativas
  • Siga convenciones de nomenclatura coherentes
  • Mantener los tipos centrados y cohesionados

Estrategia de validación

  • Añada restricciones adecuadas para cada campo
  • Defina explícitamente los campos obligatorios
  • Utilice validadores de formato para los formatos estándar
  • Considerar reglas de validación específicas del dominio

Diseño de servicios

  • Agrupe las funciones relacionadas en servicios
  • Mantener el alcance del servicio centrado y cohesionado
  • Utilizar nombres de servicio claros y descriptivos
  • Documentar la finalidad y el uso del servicio

Diseño de métodos

  • Utilizar nombres de métodos claros y orientados a la acción
  • Proporcionar descripciones detalladas
  • Definir respuestas de error adecuadas
  • Tener en cuenta los requisitos de validación

Diseño HTTP

  • Utilizar patrones de URL coherentes
  • Seguir las convenciones RESTful
  • Elegir códigos de estado apropiados
  • Gestionar los errores de forma coherente

Seguridad

  • Utilice esquemas de seguridad apropiados para su caso de uso
  • Implemente una validación de tokens adecuada
  • Defina jerarquías de alcance claras
  • Utilice HTTPS en producción