smithy4s

repository·series/0.19·Indexed 19 days ago

https://github.com/disneystreaming/smithy4s

A Scala toolset for working with Smithy models, focused on code generation and model manipulation. It includes a CLI for generating Scala code and OpenAPI specifications, a core library providing Blob and Document types for binary and structured data, and support for executing AWS service calls via the AwsCall trait.

Tokens
62.2K
Snippets
186
Records
248
Agent score
63%

What's inside smithy4s

  1. The SimpleRestJson protocol overview

    series/0.19

    The SimpleRestJson protocol is a custom Json-in/Json-out protocol for Smithy services. It uses jsoniter-scala for (de)serialization of HTTP bodies.

    Semantics:

    • Values in shapes are bound to HTTP metadata or body according to standard Http Binding traits.
    • The @mediaType trait is ignored; all bodies are serialized as JSON.

    Smithy4s provides an opt-in http4s-specific module to quickly derive HTTP services and clients using this protocol.

  2. What is a protocol in Smithy?

    series/0.19

    In the context of Smithy, a protocol is a set of concrete rules that define how data modeled in Smithy is transcribed into lower-level semantics (like HTTP).

    Smithy is designed to be protocol-agnostic. This means the core language defines the structure of your API, while protocols provide the specific implementation details. To bridge the two, protocols rely on trait annotations. For example, the alloy#simpleRestJson protocol uses the smithy.api#http trait to determine which HTTP endpoint maps to a specific Smithy operation shape. Without these traits, the protocol-specific semantics cannot be applied to the model.

  3. What is IOLocal?

    series/0.19

    IOLocal (from cats-effect) is a construct that allows for sharing context across the scope of a Fiber.

    • Scope: A value set in an IOLocal is accessible across the current Fiber.
    • Inheritance: When a Fiber is forked, the value is carried over to the new Fiber.
    • Isolation: A new Fiber cannot update the value held by its parent or sibling fibers.

    This makes it ideal for request-scoped context in web servers, where each request is handled within its own fiber hierarchy.

  4. Use Hints to access Smithy trait metadata

    series/0.19

    Smithy4s translates Smithy traits (annotations) into smithy4s.Hints. A Hints instance is a polymorphic map keyed by ShapeTag (a uniquely identified tag using referential equality).

    Every Schema can hold a Hints instance, allowing you to query trait values during serialization or deserialization. For example, the smithy.api#jsonName trait is translated to a smithy.api.JsonName Scala type that can be queried from the Hints instance to customize JSON field names.

  5. Understand the Smithy4s high-level philosophy and data flow

    series/0.19

    Smithy4s is designed to derive client stubs and server routers by interpreting generated code. It uses polymorphic interpreters that operate on generated interfaces and schemas to transform high-level method calls into low-level requests (and vice versa).

    Client-side Data Flow

    To turn a method call into a network request, the flow is:

    1. Method Call: kvstore.get("key")
    2. Initial Encoding: Convert the call into a data instance (e.g., KVStoreOp.Get("key")).
    3. Schema Retrieval: Get the Schemas (input/output) for that operation.
    4. Encoding: Compile the input schema into an encoding function (GetInput => Request).
    5. Transport: Execute the request via a low-level client (Request => Response).
    6. Decoding: Compile the output schema into a decoding function (Response => Output).

    Server-side Data Flow

    To turn a network request into a method call, the flow is:

    1. Routing: Match a Request to an operation (e.g., via HTTP path).
    2. Schema Retrieval: Get the Schemas for the matched operation.
    3. Decoding: Compile a decoding function (Request => GetInput) and run it.
    4. Reification: Recreate the operation instance (e.g., KVStoreOp.Get).
    5. Final Encoding: Use the final-encoded dual to call the user-implemented method (KVStore#get).
    6. Encoding: Compile the method's output into a response (GetOutput => Response).
  6. Warning regarding binary compatibility of Smithy4s generated code

    series/0.19

    Smithy4s prioritizes model accuracy and idiomatic Scala experience over binary compatibility. As a result, evolving a Smithy schema may break binary compatibility in the generated Scala code.

    Best Practices:

    • When using artifacts containing Smithy4s-generated code, ensure the version of Smithy4s used to produce the upstream artifact is compatible with the version used locally.
    • Use tools like MiMa to check for binary compatibility.
    • Caution: It is not recommended to treat Smithy4s-generated code as stable, publishable library material.
  7. Differentiate between null and absence of value using @nullable

    series/0.19

    By default, Smithy does not distinguish between a missing field and a field explicitly set to null. To enable this distinction (e.g., for implementing merge patch semantics), use the @alloy#nullable trait on structure members.

    In the generated Scala code, this results in a type of Option[Nullable[A]]:

    • None: The field is absent.
    • Some(Nullable.Null): The field is explicitly set to null.
    • Some(Nullable.Value(a)): The field has a specific value a.

    To convert between Option and Nullable, use Nullable.fromOption(option) and nullable.toOption.

    namespace example
    
    use alloy#nullable
    
    structure Foo {
        @nullable
        a: Integer
    }

    // Resulting Scala type: // final case class Foo(a: Option[Nullable[Int]] = None)

    // Usage: // Foo(None) => Absence of value // Foo(Some(Nullable.Null)) => Explicit null // Foo(Some(Nullable.Value(1))) => Explicit value

  8. Generate OpenAPI views for simpleRestJson services

    series/0.19

    When a Smithy service is annotated with the @simpleRestJson protocol, Smithy4s automatically generates an OpenAPI "view" for that service at build-time. This allows you to serve interactive documentation via Swagger UI.

    Example Smithy definition:

    namespace smithy4s.example
    
    use alloy#simpleRestJson
    
    @simpleRestJson
    service HelloWorldService {
      version: "1.0.0"
      operations: [Hello]
    }
  9. How to use Smithy traits for protocol-specific details

    series/0.19

    Smithy uses traits (annotations) to associate protocol-specific details, such as HTTP semantics, with data models and services. You can apply traits like @http to operations, @httpLabel to structure members to match URI path parameters, and @httpPayload to indicate the body of a request or response. Error shapes can be annotated with @error and @httpError to define client/server error types and status codes.

    namespace foo
    
    @http(method: "GET", uri: "/hello/{name}")
    operation Greet {
      input: GreetInput,
      output: GreetOutput,
      errors: [BadInput]
    }
    
    structure GreetInput {
      @httpLabel
      name: String
    }
    
    structure GreetOutput {
      @httpPayload
      message: String
    }
    
    @error("client")
    @httpError(400)
    structure BadInput {
      @jsonName("oops")
      message: String
    }