id | title |
---|---|
emitter-metadata-handling |
Handling metadata and visibility in emitters for REST API |
It's important that all emitters for REST API handle automatic visibility and metadata consistently. Make sure to read through the TypeSpec-author documentation of these features to understand how they work. This document will cover how to incorporate them correctly into your own emitter.
The standard @typespec/rest
library provides JavaScript API for emitters to interpret API written using its decorators. We'll look at the API that are particularly relevant to these features.
Note that when we say that emitters must handle things consistently, we mean that they must agree on how data is sent and received over the wire. After all, a TypeSpec specification must be able to serve as a source-of-truth on these details. Nevertheless, emitters are still free to abstract things above this level and to make different choices in doing so. For example, the OpenAPI emitter will sometimes split a single TypeSpec model into multiple suffixed schemas with names like UserCreate
and UserUpdate
while a client SDK emitter may choose to emit a single User
class that that can be serialized to a request or deserialized from a response with different fields present in different cases. In fact, these features were designed specifically to allow a TypeSpec specification to be written in terms of logical entities that emitters could then preserve.
If you haven't written an emitter before, start with emitter basics.
Then look at the REST metadata emitter sample. This emitter sample uses all of the API discussed below to write out a simple textual representation. It deliberately does not split types like the OpenAPI emitter in order to emphasize that this is not required. Instead, it adds contextual remarks to denote how data depends on context.
However, if your emitter does want to split types as OpenAPI does, then it will still use the same API. Cross-referencing with where the official [OpenAPI emitter] calls these API can also be instructive.
These are the main API involved in handling these features. See the linked API reference documentation for more details.
-
getRequestVisibility(HttpVerb): Visibility
- Use this to determine the visibility implied for data in the request parameters or body. Also note thatVisibility.Read
is always applied for response data and therefore there is no corresponding API for the response. -
MetadataInfo
- Create this once for each program usingcreateMetadataInfo(Program, MetadataInfoOptions)
then use it to reason about metadata and visibility implications with the API below. -
MetadataInfo.getEffectivePayloadType(Type, Visibility): Type
- Use this recursively on every type that is referenced. When given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed, it will recover the named model. This handles the commonly discussed case of seeing thatop something(...Thing)
receives aThing
in its request body, but also many other cases.. -
MetadataInfo.isTransformed(Model, Visibility)
- Use this to check if a type undergoes any changes in shape due to visibility or metadata. If not, this can allow for simplifications in emit. -
MetadataInfo.isPayloadProperty(ModelProperty, Visibility): boolean
- Use this to check if a property is transmitted as an object property in the payload and is not invisible or metadata sent elsewhere. -
MetadataInfo.isOptional(ModelProperty, Visibility): boolean
- Use this to determine if a property is optional for the given visibility. This will differ fromModelProperty.isOptional
when the Visibility is Update in which case the property is always considered optional. -
Visibility.Item
- Add this flag when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.