From a0beacf3879cb3c1f2e4d0920c8de72ecebeeca9 Mon Sep 17 00:00:00 2001 From: Andrew Fischer Date: Fri, 24 Jun 2022 11:44:49 -0700 Subject: [PATCH 1/2] docs: update README with an example TICKET: BG-50225 fix: format files Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Eric Crosson <67922293+ericcrosson-bitgo@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Eric Crosson <67922293+ericcrosson-bitgo@users.noreply.github.com> Update packages/io-ts-http/README.md Co-authored-by: Vivian McCarty <107434158+vmccarty@users.noreply.github.com> --- packages/io-ts-http/README.md | 296 +++++++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 1 deletion(-) diff --git a/packages/io-ts-http/README.md b/packages/io-ts-http/README.md index ee9f1671..09ad0199 100644 --- a/packages/io-ts-http/README.md +++ b/packages/io-ts-http/README.md @@ -2,8 +2,42 @@ Runtime types for (de)serializing HTTP requests from both the client and server side +## Contents + +- [@api-ts/io-ts-http](#api-tsio-ts-http) + - [Contents](#contents) + - [Preface](#preface) + - [Introduction](#introduction) + - [Overview](#overview) + - [Example](#example) + - [`apiSpec`](#apispec) + - [`httpRoute`](#httproute) + - [`path`](#path) + - [`method`](#method) + - [`request`](#request) + - [`response`](#response) + - [`httpRequest`](#httprequest) + - [`params`](#params) + - [`query`](#query) + - [`headers`](#headers) + - [`body`](#body) + - [Decoding an `httpRequest`](#decoding-an-httprequest) + - [Documentation](#documentation) + +## Preface + This package extends [io-ts](https://github.com/gcanti/io-ts) with functionality useful -for typing http requests. Start there for base knowledge required to use this package. +for typing HTTP requests. Start there for base knowledge required to use this package. + +## Introduction + +You can use @api-ts/io-ts-http to define types for APIs that can parse HTTP requests at runtime. +runtime. Most often, these types are defined in a sibiling package to a service, and +used by that service and it's clients; naming convention dictates for a service named +`` the types package will be named `-types`. Both clients +and services can use the types defined in `-types` to send and parse +requests with well-defined expectations. If requests don't adhere to the defined types +at runtime, errors may occur when those requests parse. ## Overview @@ -19,6 +53,9 @@ interface GenericHttpRequest { query: { [K: string]: string | string[]; }; + headers: { + [K: string]: string; + }; body?: unknown; } ``` @@ -81,6 +118,263 @@ possible to encode the API contract (or at least as much of it that is possible express in the type system) and therefore someone calling the API can be confident that the server will correctly interpret a request if the arguments typecheck. +## Example + +Lets drill into the `*-types` package for an example service called `message-user` +service. The top-level export for any `*-types` package is an +[`apiSpec`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/apiSpec.md). +The following is a `message-user-types` definition: + +### `apiSpec` + +```typescript +import { apiSpec } from '@api-ts/io-ts-http'; + +import { GetMessage, CreateMessage } from './routes/message'; +import { GetUser, CreateUser, UpdateUser, DeleteUser } from './routes/user'; + +/** + * message-user service + * + * @version 1.0.0 + */ +export const API = apiSpec({ + 'api.v1.message': { + get: GetMessage, + post: CreateMessage, + }, + 'api.v1.user': { + get: GetUser, + post: CreateUser, + put: UpdateUser, + delete: DeleteUser, + }, +}); +``` + +The `apiSpec` is imported, along with some named `httpRoute`s (`{Get|Create}Message`, and +`{Get|Create|Update|Delete}User`) [which we'll discuss below](#httproute). + +> Currently, if you add the `@version` JSDoc tag to the exported API spec, it will be +> used as the API `version` when generating an OpenAPI schema. Support for other tags +> may be added in the future. + +The top-level export for `message-user-types` is `API`, which we define as an `apiSpec` +with two endpoints `api/v1/message` and `api/v1/user`. The `api/v1/message` endpoint responds to `GET` and +`POST` verbs while the second reponds to `GET`, `POST`, `PUT`, and `DELETE` verbs using +`httpRoute`s defined in `./routes/message`. The following are the `httpRoute`s defined in +`./routes/message`. + +### `httpRoute` + +```typescript +import * as t from 'io-ts'; +import { httpRoute, httpRequest } from '@api-ts/io-ts-http'; + +export const GetMessage = httpRoute({ + path: '/message/{id}', + method: 'GET', + request: httpRequest({ + params: { + id: t.string, + }, + }), + response: { + 200: t.type({ + id: t.string, + message: t.string, + }), + 404: t.type({ + error: t.string, + }), + }, +}); + +export const CreateMessage = httpRoute({ + path: '/message', + method: 'POST', + request: httpRequest({ + body: { + message: t.string, + }, + }), + response: { + 200: t.type({ + id: t.string, + message: t.string, + }), + 404: t.type({ + error: t.string, + }), + }, +}); +``` + +The first import is the `io-ts` package. It's usually imported `as t` for use in +describing the types of data properties. Again, review +[io-ts](https://github.com/gcanti/io-ts) documentation for more context on how to use it +and this package. + +Then `httpRoute` and `httpRequest` are imported. We'll review the +[`httpRequest`](#httprequest) below, but first, let's review the `GetMessage` `httpRoute`. + +```typescript +export const GetMessage = httpRoute({ + path: '/message/{id}', + method: 'GET', + request: httpRequest({ + params: { + id: t.string, + }, + }), + response: { + 200: t.type({ + id: t.string, + message: t.string, + }), + 404: t.type({ + error: t.string, + }), + }, +}); +``` + +[`httpRoute`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/httpRoute.md)s +`apiSpec`s use [`httpRoute`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/httpRoute.md)s to define the `path`, `method`, `request` and `response` of a +route. + +#### `path` + +The route's `path` along with possible path variables. You should surround variables +with brackets like `{name}`, and are to the `request` codec under the `params` property. + +#### `method` + +The route's `method` is the +[HTTP request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) to use +for that route. In our `GetMessage` example, the `method` is `GET`, while in our +`PostMessage` example, the `method` is `POST`. + +#### `request` + +The route's `request` is the output of the `httpRequest` function. This will be +described below. + +#### `response` + +The route's `response` describes the possible +[HTTP responses](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) the route can +produce. The key-value pairs of the `response` object are an HTTP status code followed +by the `io-ts` type of the response body. In our `GetMessage` example, a `200` status +response yields a payload of a JSON object with two properties, `message` which is a +`string` and `id` which is also a `string`, and a `404` yeilds a payload of a JSON +object with a single property `error` which is a `String`. + +### `httpRequest` + +Use `httpRequest` to build the expected type that you pass in a request to the route. +In our example `GetMessage` + +```typescript +... +export const GetMessage = httpRoute({ + path: '/message/{id}', + method: 'GET', + request: httpRequest({ + params: { + id: t.string + }, + }), +... +``` + +`httpRequest`s have a total of 4 optional properties: `params` (shown +in the example), `query`, `headers`, and `body`. + +#### `params` + +`params` is an object representing the types of path parameters in a URL. Any URL +parameters in the `path` property of an `httpRoute` must be accounted for in the +`params` property of the `httpRequest`. Our request has a single URL parameter it is +expecting, `id`. This is the type of this parameter is captured in the `params` object +of our `httpRequest`. + +#### `query` + +`query` is the object representing the values passed in via query parameters at the end +of a URL. The following example uses a new route, `GetMessages`, to our API that searches messages +related to a specific `author`: + +```typescript +... +export const GetMessages = httpRoute({ + path: '/messages', + method: 'GET', + request: httpRequest({ + query: { + author: t.string + }, + }), +... +}); +``` + +#### `headers` + +`headers` is an object representing the types of the +[HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) passed in with +a request. + +#### `body` + +`body` is an object representing the type of the +[HTTP body](https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#body) of a +request. Often this is a JSON object. The `CreateMessage` `httpRoute` in our example +uses the `body` property: + +```typescript +export const CreateMessage = httpRoute({ + path: '/message', + method: 'POST', + request: httpRequest({ + body: { + message: t.string + } + }), +... +}); +``` + +#### Decoding an `httpRequest` + +When you decode `httpRequests` using `io-ts` helpers, the properties of the request are +flattened like this: + +```typescript +import { DateFromString, NumberFromString } from 'io-ts-types'; + +// build an httpRequest with one parameter id and a body with content and a timestamp +const Request = httpRequest({ + params: { + id: NumberFromString, + }, + body: { + content: t.string, + timestamp: DateFromISOString, + }, +}); + +// use io-ts to get the type of the Request +type Request = t.TypeOf; + +// same as +type Request = { + id: number; + content: string; + timestamp: Date; +}; +``` + ## Documentation - [API Reference](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/apiReference.md) From 5ef623d643ae26737eaf308101ae12634be2a051 Mon Sep 17 00:00:00 2001 From: Andrew Fischer Date: Mon, 27 Jun 2022 13:42:20 -0700 Subject: [PATCH 2/2] docs: fix grammar issues docs: make intro more clear Co-authored-by: Eric Crosson <67922293+ericcrosson-bitgo@users.noreply.github.com> docs: describe more clearly what io-ts-http is used for Co-authored-by: Eric Crosson <67922293+ericcrosson-bitgo@users.noreply.github.com> --- packages/io-ts-http/README.md | 71 +++++++++++++++++------------------ 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/packages/io-ts-http/README.md b/packages/io-ts-http/README.md index 09ad0199..d45866b8 100644 --- a/packages/io-ts-http/README.md +++ b/packages/io-ts-http/README.md @@ -31,17 +31,15 @@ for typing HTTP requests. Start there for base knowledge required to use this pa ## Introduction -You can use @api-ts/io-ts-http to define types for APIs that can parse HTTP requests at runtime. -runtime. Most often, these types are defined in a sibiling package to a service, and -used by that service and it's clients; naming convention dictates for a service named -`` the types package will be named `-types`. Both clients -and services can use the types defined in `-types` to send and parse -requests with well-defined expectations. If requests don't adhere to the defined types -at runtime, errors may occur when those requests parse. +io-ts-http is the definition language for api-ts specifications, which define the API +contract for a web sever to an arbitrary degree of precision. Web servers can use the +io-ts-http spec to parse HTTP requests at runtime, and encode HTTP responses. Clients +can use the io-ts-http spec to enforce API compatibility at compile time, and to encode +requests to the server and decode responses. ## Overview -The primary function in this library is `httpRequest`, which is used to build codecs +The primary function in this library is `httpRequest`. You can use this to build codecs which can parse a generic HTTP request into a more refined type. The generic HTTP request should conform to the following interface: @@ -120,10 +118,10 @@ the server will correctly interpret a request if the arguments typecheck. ## Example -Lets drill into the `*-types` package for an example service called `message-user` -service. The top-level export for any `*-types` package is an -[`apiSpec`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/apiSpec.md). -The following is a `message-user-types` definition: +Let's define the api-ts spec for a hypothetical `message-user` service. The conventional +top-level export is an +[`apiSpec`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/apiSpec.md) +value; for example: ### `apiSpec` @@ -152,18 +150,18 @@ export const API = apiSpec({ }); ``` -The `apiSpec` is imported, along with some named `httpRoute`s (`{Get|Create}Message`, and -`{Get|Create|Update|Delete}User`) [which we'll discuss below](#httproute). +The `apiSpec` is imported, along with some named `httpRoute`s (`{Get|Create}Message`, +and `{Get|Create|Update|Delete}User`) [which we'll discuss below](#httproute). > Currently, if you add the `@version` JSDoc tag to the exported API spec, it will be > used as the API `version` when generating an OpenAPI schema. Support for other tags > may be added in the future. The top-level export for `message-user-types` is `API`, which we define as an `apiSpec` -with two endpoints `api/v1/message` and `api/v1/user`. The `api/v1/message` endpoint responds to `GET` and -`POST` verbs while the second reponds to `GET`, `POST`, `PUT`, and `DELETE` verbs using -`httpRoute`s defined in `./routes/message`. The following are the `httpRoute`s defined in -`./routes/message`. +with two endpoints `api/v1/message` and `api/v1/user`. The `api/v1/message` endpoint +responds to `GET` and `POST` verbs while the second reponds to `GET`, `POST`, `PUT`, and +`DELETE` verbs using `httpRoute`s defined in `./routes/message`. The following are the +`httpRoute`s defined in `./routes/message`. ### `httpRoute` @@ -216,7 +214,8 @@ describing the types of data properties. Again, review and this package. Then `httpRoute` and `httpRequest` are imported. We'll review the -[`httpRequest`](#httprequest) below, but first, let's review the `GetMessage` `httpRoute`. +[`httpRequest`](#httprequest) below, but first, let's review the `GetMessage` +`httpRoute`. ```typescript export const GetMessage = httpRoute({ @@ -240,8 +239,9 @@ export const GetMessage = httpRoute({ ``` [`httpRoute`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/httpRoute.md)s -`apiSpec`s use [`httpRoute`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/httpRoute.md)s to define the `path`, `method`, `request` and `response` of a -route. +`apiSpec`s use +[`httpRoute`](https://github.com/BitGo/api-ts/blob/master/packages/io-ts-http/docs/httpRoute.md)s +to define the `path`, `method`, `request` and `response` of a route. #### `path` @@ -272,24 +272,24 @@ object with a single property `error` which is a `String`. ### `httpRequest` -Use `httpRequest` to build the expected type that you pass in a request to the route. -In our example `GetMessage` +Use `httpRequest` to build the expected type that you pass in a request to the route. In +our example `GetMessage` ```typescript -... export const GetMessage = httpRoute({ path: '/message/{id}', method: 'GET', request: httpRequest({ params: { - id: t.string + id: t.string, }, }), -... + // ... +}); ``` -`httpRequest`s have a total of 4 optional properties: `params` (shown -in the example), `query`, `headers`, and `body`. +`httpRequest`s have a total of 4 optional properties: `params` (shown in the example), +`query`, `headers`, and `body`. #### `params` @@ -302,20 +302,19 @@ of our `httpRequest`. #### `query` `query` is the object representing the values passed in via query parameters at the end -of a URL. The following example uses a new route, `GetMessages`, to our API that searches messages -related to a specific `author`: +of a URL. The following example uses a new route, `GetMessages`, to our API that +searches messages related to a specific `author`: ```typescript -... export const GetMessages = httpRoute({ path: '/messages', method: 'GET', request: httpRequest({ query: { - author: t.string + author: t.string, }, }), -... + // ... }); ``` @@ -338,10 +337,10 @@ export const CreateMessage = httpRoute({ method: 'POST', request: httpRequest({ body: { - message: t.string - } + message: t.string, + }, }), -... + // ... }); ```