TypeSpec Documentation

website·Indexed 35 days ago

https://typespec.io/docs

Documentation for TypeSpec, a language for defining API schemas. It covers language basics including models, operations, decorators, and templates, as well as guides for REST APIs, OpenAPI v2 and v3 mapping, and support for JSON Schema, Protobuf, and Events.

Tokens
44.5K
Snippets
341
Records
370
Agent score
71%

What's inside TypeSpec

  1. Overview of TypeSpec Visibility

    Visibility is a TypeSpec language feature used to share a single model across multiple operations while controlling which properties are visible in specific contexts. It allows the definition of different "views" of a model. Visibility applies only to model properties and is managed via visibility classes (enums). Emitters use these flags to include or exclude properties in the generated output.
  2. Understand Http.File semantics

    The @typespec/http library provides a special model, TypeSpec.Http.File, used to represent files. Unlike ordinary models that would be interpreted as structured JSON, Http.File signals that an operation has a binary payload with an arbitrary content-type.

    Http.File contains three special properties:

    • contents: The actual binary body of the request, response, or multipart payload. This location cannot be changed by subtypes.
    • contentType: (Optional) The MIME type sent in the Content-Type header. This location cannot be changed by subtypes.
    • filename: (Optional) The name of the file sent in the filename parameter of the Content-Disposition header. This is valid for responses and multipart payloads, but not requests. This location can be overridden by subtypes using HTTP metadata.
  3. Use decorators to attach metadata to TypeSpec types

    Decorators in TypeSpec allow developers to attach metadata to types or compute types based on inputs. Most declarations that create a Type can be decorated, including namespaces, interfaces, operations (and their parameters), scalars, and models (and their members).

    Constraints:

    • Aliases cannot be decorated because they do not create new Types.
    • Type expressions (such as unions using |) and anonymous models cannot be decorated as they are not declarations.
  4. Define OpenAPI v3 Request Bodies in TypeSpec

    In TypeSpec, an OpenAPI v3 Request Body object is represented as an operation parameter decorated with @body.

    • Description: Use the @doc decorator.
    • Required/Optional: A ? following the parameter name indicates required: false; otherwise, it is required: true.
    • Content Type: Specified using a @header contentType parameter. If multiple values are provided in a union, the resulting OpenAPI content object will have one entry for each value.
    • Multiple Schemas: To define multiple content entries with different schemas for the same path and method, use the @sharedRoute decorator to define separate operations.
    // Single content type
    @put op uploadImage(@header contentType: "image/png", @body image: bytes): void;
    
    // Multiple content types (union)
    @post op analyze(
      @header contentType: "application/octet-stream" | "application/pdf" | "image/jpeg",
      @body image: bytes,
    ): string | Error;
    
    // Multiple content entries with different schemas using @sharedRoute
    @route(":process")
    namespace Process {
      @sharedRoute
      @post
      op process(...Widget): Widget | Error;
    
      model CsvBody {
        @header contentType: "text/csv";
        @body _: string;
      }
      @sharedRoute
      @post
      op process2(...CsvBody): Widget | Error;
    }
  5. Add new operations to a specific API version

    Use the @added decorator to introduce a new operation starting from a specific version. The TypeSpec definition should reflect the current state of the API, while the decorator marks the version where the operation first became available.
    enum Versions {
      v1,
      v2,
    }
    
    @route("/widget")
    op list(): Widget[] | Error;
    
    @added(Versions.v2)
    @route("/widget/{id}")
    op get(...Resource.KeysOf<Widget>): Widget | Error;
  6. Apply hierarchical routing to namespaces and interfaces

    The @route decorator can be applied to a parent namespace or interface. All nested operations, interfaces, and namespaces will automatically be prefixed with the parent's route, allowing for modular API path definitions.
    @route("/store")
    namespace PetStore {
      op hello(): void; // Resulting route: `/store`
      @route("ping") op ping(): void; // Resulting route: `/store/ping`
    
      @route("/pets")
      interface Pets {
        list(): Pet[]; // Resulting route: `/store/pets`
        @route("{petId}") read(petId: string): Pet; // Resulting route: `/store/pets/{petId}`
      }
    }
  7. Create custom File models

    Custom file types can be created by providing arguments to the Http.File template or extending it. This is used to restrict allowed media types (Content-Type) or define textual files.

    Note: You can override existing properties of Http.File via extension, but you cannot define additional properties.

    // Narrowing allowed media types using an alias
    alias ImageFile = File<"image/png" | "image/jpeg">;
    
    // Narrowing using a model extension
    model ImageFile extends File<"image/png" | "image/jpeg"> {}
    
    // Alternative extension by overriding the contentType property
    model ImageFile extends File {
      contentType: "image/png" | "image/jpeg";
    }
  8. Create scalar values using constructors

    Scalars can be declared with init constructors to record how a specific value should be created. These constructors do not contain runtime code; they serve as metadata for emitters to generate the correct value in the target language.
    scalar ipv4 extends string {
      init fromInt(value: uint32);
    }
    
    const homeIp = ipv4.fromInt(2130706433);
  9. Reference enum members and union variants as values

    References to enum members and union variants behave like scalar literals: they are treated as types in a type context and as values in a value or ambiguous context. Note: It is an error to refer to a union variant as a value if its associated type is not a literal type.
    enum Color {
      red, green, blue
    }
    @setColorValue(Color.red) // treated as value "red"
    
    union ColorUnion {
      red: "red",
      other: string,
    }
    @setColorValue(ColorUnion.red)   // ok: passes scalar "red"
    @setColorValue(ColorUnion.other) // error: 'other' is a type (string), not a literal value
  10. Define path and query parameters in TypeSpec operations

    Use the @path and @query decorators to specify how operation parameters should be passed in an HTTP request. Parameters marked with @query are passed as query string parameters, while parameters marked with @path are passed as part of the URL path.
    @route("/pets")
    namespace Pets {
      op list(@query skip: int32, @query top: int32): Pet[];
      op read(@path petId: int32): Pet;
    }
  11. Manage authentication overrides in the application hierarchy

    The @useAuth decorator follows a hierarchical override pattern. A decorator applied to a child type (e.g., an operation) overrides the security scheme of its parent (e.g., a namespace). To extend rather than replace the parent's security, you must explicitly re-specify the parent's schemes in the child's decorator.
    @useAuth(BasicAuth)
    namespace MyService {
      // Overrides BasicAuth: only ApiKey is used
      @useAuth(ApiKeyAuth<ApiKeyLocation.query, "api_key">
      op one(): void;
    
      // Inherits BasicAuth
      op two(): void;
    
      // Extends BasicAuth: accepts either Basic OR ApiKey
      @useAuth(BasicAuth | ApiKeyAuth<ApiKeyLocation.query, "api_key">
      op three(): void;
    }
  12. Handle additional properties using Record<T>

    The Record<T> model allows a model to have an arbitrary number of properties of type T. This can be combined with named models in three ways:

    1. Spread (...): Includes explicitly defined properties plus any additional properties of type T. Explicit properties can have types incompatible with T.
    2. Is (is): All properties in the model, including explicitly defined ones, must be of type T.
    3. Extends (extends): Similar semantics to is, but establishes a formal relationship between models. is Record<T> is generally recommended over extends.
    // Using spread: age can be int32 while others are strings
    model Person {
      age: int32;
      ...Record<string>;
    }
    
    // Using is: all properties must be strings
    model PersonIsRecord is Record<string> {
      name: string;
    }