Skip to content

Commit b429893

Browse files
committed
Add client codegen guide to docs
A section under 'Guides' was added to the docs, 'Generating code'. It covers updating the model from the Quick start guide to enable codegen, adding the client codegen plugin, and using the generated client. The example uses the TypeScript code generator. A hyperlink target was also added to the implementations doc for the 'Client code generators' section to make it easier to refer to in the new guide.
1 parent f5a0893 commit b429893

File tree

6 files changed

+335
-0
lines changed

6 files changed

+335
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
.. _generating-a-client:
2+
3+
===================
4+
Generating a client
5+
===================
6+
7+
Smithy code generators are implemented as :ref:`smithy-build plugins <plugins>`
8+
that run when the Smithy model is built. The plugins generate the equivalent of
9+
packages in their respective languages, configured to be used by language
10+
idiomatic tooling.
11+
12+
Add the Codegen Plugin
13+
======================
14+
15+
TypeScript clients are generated by the ``typescript-codegen`` plugin. Configure
16+
the plugin by adding it to ``smithy-build.json``:
17+
18+
.. code-block:: json
19+
20+
{
21+
"version": "1.0",
22+
"plugins": {
23+
"typescript-codegen": {
24+
"package": "@weather-service/client",
25+
"packageVersion": "0.0.1"
26+
}
27+
}
28+
}
29+
30+
In this case, we've configured the ``typescript-codegen`` plugin to generate a package
31+
named ``@weather-service/client`` with version ``0.0.1``.
32+
33+
Next, add a build-time dependency on the code generator:
34+
35+
.. code-block:: kotlin
36+
37+
buildscript {
38+
repositories {
39+
mavenCentral()
40+
}
41+
dependencies {
42+
classpath("software.amazon.smithy.typescript:smithy-aws-typescript-codegen:0.12.0")
43+
}
44+
}
45+
46+
`smithy-aws-typescript-codegen` is used here because it provides a protocol generator for
47+
the :ref:`@aws.protocols#restJson1 <aws.protocols#restJson1-trait>` protocol. As
48+
mentioned in :doc:`update-model`, code generators must know how to generate code for
49+
the specified protocol.
50+
51+
Now run ``gradle build`` to build the model and generate the code. The TypeScript
52+
package is written to the ``typescript-codegen`` directory::
53+
54+
.
55+
├── build
56+
│ ├── smithyprojections
57+
│ │ └── weather-service
58+
│ │ └── source
59+
│ │ ├── build-info/
60+
│ │ ├── model/
61+
│ │ ├── sources/
62+
│ │ └── typescript-codegen
63+
│ │ ├── LICENSE
64+
│ │ ├── package.json
65+
│ │ ├── src/
66+
│ │ ├── tsconfig.cjs.json
67+
│ │ ├── tsconfig.es.json
68+
│ │ ├── tsconfig.json
69+
│ │ ├── tsconfig.types.json
70+
│ │ └── typedoc.json
71+
│ └── tmp
72+
├── build.gradle.kts
73+
├── model
74+
│ └── weather.smithy
75+
└── smithy-build.json
76+
77+
Using the generated code
78+
========================
79+
80+
The generated code is just a normal TypeScript package. Each time the model
81+
is built and the code generated, the TypeScript code also has to be compiled.
82+
The generated ``package.json`` contains scripts to do so:
83+
84+
.. code-block:: json
85+
86+
"scripts": {
87+
"build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'",
88+
"build:cjs": "tsc -p tsconfig.cjs.json",
89+
"build:docs": "typedoc",
90+
"build:es": "tsc -p tsconfig.es.json",
91+
"build:types": "tsc -p tsconfig.types.json",
92+
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
93+
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
94+
"prepack": "yarn run clean && yarn run build"
95+
}
96+
97+
This example creates a mono-repo using `Yarn Workspaces`_ that
98+
integrates building the Smithy model and generating the code into the
99+
development workflow. First, move the Smithy project into its own
100+
directory named ``smithy/``::
101+
102+
.
103+
└── smithy
104+
├── build
105+
├── build.gradle.kts
106+
├── model
107+
└── smithy-build.json
108+
109+
Next, create a ``package.json`` in the root of the project with the following
110+
contents:
111+
112+
.. code-block:: json
113+
114+
{
115+
"name": "weather-service",
116+
"scripts": {
117+
"generate": "cd smithy && gradle clean build",
118+
"build": "yarn workspace @weather-service/client build",
119+
},
120+
"dependencies": {
121+
"@weather-service/client": "0.0.1"
122+
},
123+
"private": true,
124+
"workspaces": [
125+
"smithy/build/smithyprojections/smithy/client/typescript-codegen"
126+
]
127+
}
128+
129+
A few things to note:
130+
131+
* The path under ``workspaces`` is the path to the root of the generated
132+
TypeScript package.
133+
* A ``generate`` script which builds the model, re-generating the code.
134+
* The ``build`` script compiles the generated TypeScript package,
135+
referred to by the name specified in the ``typescript-codegen`` plugin
136+
configuration in ``smithy-build.json``.
137+
* A dependency has been added on the generated TypeScript package, using the
138+
name and version specified in the ``typescript-codegen`` plugin configuration
139+
in ``smithy-build.json``
140+
141+
After making model updates, use ``yarn generate && yarn build`` to run the
142+
code generator and build the generated code. You will have to do this before
143+
using the client in this example, because the output directory path has changed
144+
after moving the Smithy project into the ``smithy`` directory.
145+
146+
Finally, create an ``app.ts`` file to use the client:
147+
148+
.. code-block:: typescript
149+
150+
import {
151+
GetCityCommandInput,
152+
GetCityCommandOutput,
153+
Weather
154+
} from '@weather-service/client';
155+
156+
const client: Weather = new Weather({ endpoint: 'some-endpoint' });
157+
158+
const getCityInput: GetCityCommandInput = {
159+
cityId: 'foo'
160+
};
161+
162+
client.getCity(getCityInput).then((getCityOutput: GetCityCommandOutput) => {
163+
// TODO Handle response
164+
});
165+
166+
The ``typescript-codegen`` plugin has generated a client, ``Weather``, with methods
167+
for each of the operations, as well as types for the inputs and outputs of those
168+
operations.
169+
170+
.. _Yarn Workspaces: https://classic.yarnpkg.com/en/docs/workspaces
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.. _generating-code:
2+
3+
===============
4+
Generating code
5+
===============
6+
7+
One of Smithy's greatest strengths is code generation. Smithy models can be used to
8+
generate clients and servers in a variety of programming languages. The generated
9+
code takes care of low level details like endpoints, serialization, deserialization,
10+
and more.
11+
12+
In this guide, we will use the Smithy model we created in the
13+
:doc:`Quick start guide </quickstart>` to generate
14+
a TypeScript client that knows how to communicate with the weather service. For a
15+
list of code generators, see :ref:`Client code generators <client-code-generators>`.
16+
17+
.. toctree::
18+
:maxdepth: 1
19+
:caption: Code generation guide
20+
21+
update-model
22+
generating-a-client
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
.. _update-model:
2+
3+
=========================
4+
Updating the Smithy Model
5+
=========================
6+
7+
The weather service model is currently a high-level description of the Weather API,
8+
and doesn't contain any low-level details about how the data is sent over the wire.
9+
However, in order to communicate with the Weather API, a client needs to know some
10+
of these details. For example, how should a client serialize requests? Is the server
11+
using HTTP or something else?
12+
13+
Smithy makes it simple to add this information to a model via :ref:`traits`. Let's
14+
update the weather service model with the details needed for generating a client.
15+
16+
Specifying a protocol
17+
=====================
18+
19+
In Smithy, `protocols` define how a client and server communicate, and are modeled by
20+
:doc:`protocol traits </spec/protocol-traits>`. A service's supported protocols can be
21+
defined by applying protocol traits to the service shape. To generate code, at least
22+
one protocol trait is required, and the code generator has to know how to generate code
23+
for that protocol.
24+
25+
In this example, the weather service will use the
26+
:ref:`AWS restJson1 protocol <aws-restjson1-protocol>`, which is a RESTful protocol
27+
that sends JSON in structured payloads over HTTP.
28+
29+
The protocol trait, :ref:`@aws.protocols#restJson1 <aws.protocols#restJson1-trait>`,
30+
is provided by the `smithy-aws-traits` package, so first add a dependency to
31+
``build.gradle.kts``:
32+
33+
.. code-block:: kotlin
34+
35+
dependencies {
36+
...
37+
implementation("software.amazon.smithy:smithy-aws-traits:__smithy_version__")
38+
}
39+
40+
Now, import the :ref:`@aws.protocols#restJson1 <aws.protocols#restJson1-trait>` trait
41+
and apply it to the ``Weather`` service shape:
42+
43+
.. code-block:: smithy
44+
45+
$version: "2"
46+
namespace example.weather
47+
48+
use aws.protocols#restJson1
49+
50+
/// Provides weather forecasts.
51+
@paginated(
52+
inputToken: "nextToken"
53+
outputToken: "nextToken"
54+
pageSize: "pageSize"
55+
)
56+
@restJson1
57+
service Weather {
58+
version: "2006-03-01"
59+
resources: [City]
60+
operations: [GetCurrentTime]
61+
}
62+
63+
Adding HTTP bindings
64+
====================
65+
66+
In Smithy, HTTP can be configured by applying :ref:`HTTP binding traits <http-traits>`
67+
to operation shapes. HTTP protocols can use these traits to generate code that formats
68+
HTTP messages properly.
69+
70+
First, configure the HTTP method, request URI, and the status code of a successful
71+
response with the :ref:`@http trait <http-trait>`.
72+
73+
.. code-block:: smithy
74+
75+
@readonly
76+
@http(code: 200, method: "GET", uri: "/cities/{cityId}")
77+
operation GetCity {
78+
input: GetCityInput
79+
output: GetCityOutput
80+
errors: [NoSuchResource]
81+
}
82+
83+
@paginated(items: "items")
84+
@readonly
85+
@http(code: 200, method: "GET", uri: "/cities")
86+
operation ListCities {
87+
input: ListCitiesInput
88+
output: ListCitiesOutput
89+
}
90+
91+
@readonly
92+
@http(code: 200, method: "GET", uri: "/currentTime")
93+
operation GetCurrentTime {
94+
input: GetCurrentTimeInput
95+
output: GetCurrentTimeOutput
96+
}
97+
98+
@readonly
99+
@http(code: 200, method: "GET", uri: "/forecast/{cityId}")
100+
operation GetForecast {
101+
input: GetForecastInput
102+
output: GetForecastOutput
103+
}
104+
105+
The URI patterns for the ``GetCity`` and ``GetForecast`` operations each use an HTTP label to
106+
bind the ``cityId`` member of the operation input structure to the request URI. Let's specify
107+
the members that should be bound to the URIs using the :ref:`@httpLabel trait <httplabel-trait>`:
108+
109+
.. code-block:: smithy
110+
111+
@input
112+
structure GetCityInput {
113+
// "cityId" provides the identifier for the resource and
114+
// has to be marked as required.
115+
@required
116+
@httpLabel
117+
cityId: CityId
118+
}
119+
120+
@input
121+
structure GetForecastInput {
122+
@required
123+
@httpLabel
124+
cityId: CityId
125+
}
126+
127+
For the ``ListCities`` operation, include the ``nextToken`` and ``pageSize`` input members
128+
in the request URI as query parameters using the :ref:`@httpQuery trait <httpquery-trait>`:
129+
130+
.. code-block:: smithy
131+
132+
@input
133+
structure ListCitiesInput {
134+
@httpQuery("nextToken")
135+
nextToken: String
136+
@httpQuery("pageSize")
137+
pageSize: Integer
138+
}

docs/source-2.0/guides/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Guides
66
:maxdepth: 1
77

88
building-models/index
9+
generating-code/index
910
model-linters
1011
evolving-models
1112
style-guide

docs/source-2.0/implementations.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ Build tooling
126126
- The Smithy SBT plugin transforms Smithy specifications into
127127
protocol-agnostic Scala clients and servers.
128128

129+
.. _client-code-generators:
130+
129131
----------------------
130132
Client code generators
131133
----------------------

docs/source-2.0/quickstart.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ That's it! We just created a simple, read-only, ``Weather`` service.
491491
3. Try adding :ref:`HTTP binding traits <http-traits>` to the API.
492492
4. Try adding :ref:`tags <tags-trait>` to shapes and filtering them out with
493493
:ref:`excludeShapesByTag <excludeShapesByTag-transform>`.
494+
5. Follow the :ref:`Code Generation Guide <generating-code>` to generate
495+
code for the ``Weather`` service.
494496

495497
There's plenty more to explore in Smithy. The
496498
:ref:`Smithy specification <smithy-specification>` can teach you everything you

0 commit comments

Comments
 (0)