mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Dev docs] Added final section to HTTP versioning tutorial (#154901)
## Summary Adds the final section to the HTTP versioning tutorial about using the route versioning specification.
This commit is contained in:
parent
ebe278490f
commit
55b9fd2353
1 changed files with 110 additions and 7 deletions
|
@ -41,7 +41,7 @@ router.get(
|
|||
);
|
||||
```
|
||||
|
||||
#### Why is this problemetic for versioning?
|
||||
#### Why is this problematic for versioning?
|
||||
|
||||
Whenever we perform a data migration the body of this endpoint will change for all clients. This prevents us from being able to maintain past interfaces and gracefully introduce new ones.
|
||||
|
||||
|
@ -119,7 +119,7 @@ router.post(
|
|||
}
|
||||
);
|
||||
```
|
||||
#### Why is this problemetic for versioning?
|
||||
#### Why is this problematic for versioning?
|
||||
|
||||
This HTTP API currently accepts all numbers and strings as input which allows for unexpected inputs like negative numbers or non-URL friendly characters. This may break future migrations or integrations that assume your data will always be within certain parameters.
|
||||
|
||||
|
@ -141,7 +141,7 @@ This HTTP API currently accepts all numbers and strings as input which allows fo
|
|||
|
||||
Adding this validation we negate the risk of unexpected values. It is not necessary to use `@kbn/config-schema`, as long as your validation mechanism provides finer grained controls than "number" or "string".
|
||||
|
||||
In summary: think about the acceptable paramaters for every input your HTTP API expects.
|
||||
In summary: think about the acceptable parameters for every input your HTTP API expects.
|
||||
|
||||
### 3. Keep interfaces as "narrow" as possible
|
||||
|
||||
|
@ -170,7 +170,7 @@ router.get(
|
|||
|
||||
The above code follows guidelines from steps 1 and 2, but it allows clients to specify ANY string by which to sort. This is a far "wider" API than we need for this endpoint.
|
||||
|
||||
#### Why is this problemetic for versioning?
|
||||
#### Why is this problematic for versioning?
|
||||
|
||||
Without telemetry it is impossible to know what values clients might be passing through — and what type of sort behaviour they are expecting.
|
||||
|
||||
|
@ -207,9 +207,112 @@ router.get(
|
|||
|
||||
The changes are:
|
||||
|
||||
1. New input validation accepts a known set of values. This makes our HTTP API far _narrower_ and specific to our use case. It does not matter that our `sortSchema` has the same values as our persistence schema, what matters is that we created a **translation layer** between our HTTP API and our internal schema. This faclitates easily versioning this endpoint.
|
||||
1. New input validation accepts a known set of values. This makes our HTTP API far _narrower_ and specific to our use case. It does not matter that our `sortSchema` has the same values as our persistence schema, what matters is that we created a **translation layer** between our HTTP API and our internal schema. This facilitates easily versioning this endpoint.
|
||||
2. **Bonus point**: we use the `escapeKuery` utility to defend against KQL injection attacks.
|
||||
|
||||
### 4. Use the versioned API spec
|
||||
### 4. Adhere to the HTTP versioning specification
|
||||
|
||||
_Under construction, check back here soon!_
|
||||
#### Choosing the right version
|
||||
|
||||
##### Public endpoints
|
||||
Public endpoints include any endpoint that is intended for users to directly integrate with via HTTP.
|
||||
|
||||
Choose a date string in the format `YYYY-MM-DD`. This date should be the date that a (group) of APIs was made available.
|
||||
|
||||
##### Internal endpoints
|
||||
Internal endpoints are all non-public endpoints (see definition above).
|
||||
|
||||
If you need to maintain backwards-compatibility for an internal endpoint use a single, larger-than-zero number. Ex. `1`.
|
||||
|
||||
|
||||
#### Use the versioned router
|
||||
|
||||
Core exposes a versioned router that ensures your endpoint's behaviour and formatting all conforms to the versioning specification.
|
||||
|
||||
```typescript
|
||||
router.versioned.
|
||||
.post({
|
||||
access: 'public', // This endpoint is intended for a public audience
|
||||
path: '/api/my-app/foo/{id?}',
|
||||
options: { timeout: { payload: 60000 } },
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: '2023-01-01', // The public version of this API
|
||||
validate: {
|
||||
request: {
|
||||
query: schema.object({
|
||||
name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })),
|
||||
}),
|
||||
params: schema.object({
|
||||
id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })),
|
||||
}),
|
||||
body: schema.object({ foo: schema.string() }),
|
||||
},
|
||||
response: {
|
||||
200: { // In development environments, this validation will run against 200 responses
|
||||
body: schema.object({ foo: schema.string() }),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
await ctx.fooService.create(req.body.foo, req.params.id, req.query.name);
|
||||
return res.ok({ body: { foo: req.body.foo } });
|
||||
}
|
||||
)
|
||||
// BREAKING CHANGE: { foo: string } => { fooString: string } in response body
|
||||
.addVersion(
|
||||
{
|
||||
version: '2023-02-01',
|
||||
validate: {
|
||||
request: {
|
||||
query: schema.object({
|
||||
name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })),
|
||||
}),
|
||||
params: schema.object({
|
||||
id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })),
|
||||
}),
|
||||
body: schema.object({ fooString: schema.string() }),
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
body: schema.object({ fooName: schema.string() }),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name);
|
||||
return res.ok({ body: { fooName: req.body.fooString } });
|
||||
}
|
||||
)
|
||||
// BREAKING CHANGES: Enforce min/max length on fooString
|
||||
.addVersion(
|
||||
{
|
||||
version: '2023-03-01',
|
||||
validate: {
|
||||
request: {
|
||||
query: schema.object({
|
||||
name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })),
|
||||
}),
|
||||
params: schema.object({
|
||||
id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })),
|
||||
}),
|
||||
body: schema.object({ fooString: schema.string({ minLength: 0, maxLength: 1000 }) }),
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
body: schema.object({ fooName: schema.string() }),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (ctx, req, res) => {
|
||||
await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name);
|
||||
return res.ok({ body: { fooName: req.body.fooString } });
|
||||
}
|
||||
```
|
||||
|
||||
#### Additional reading
|
||||
For a more details on the versioning specification see [this document](https://docs.google.com/document/d/1YpF6hXIHZaHvwNaQAxWFzexUF1nbqACTtH2IfDu0ldA/edit?usp=sharing).
|
Loading…
Add table
Add a link
Reference in a new issue