Skip to content

Commit 47f73d6

Browse files
authored
Improve documentation 12 (#318)
1 parent 89e3a46 commit 47f73d6

File tree

2 files changed

+24
-26
lines changed

2 files changed

+24
-26
lines changed

docs/quickstart/setup.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Setup
33

4-
Cadwyn is built around FastAPI and supports all of its functionality out of the box. One difference is that Cadwyn requires you to define API versions and extends your routing and swagger to support API versioning.
4+
Cadwyn is built around FastAPI and supports all of its functionality out of the box. One difference is that Cadwyn requires you to define API versions and extends your routing and Swagger to support API versioning.
55

66
## Installation
77

@@ -11,7 +11,7 @@ Cadwyn is built around FastAPI and supports all of its functionality out of the
1111

1212
## The basics
1313

14-
First, let's set up the most basic versioned app possible:
14+
First, set up the most basic versioned app possible:
1515

1616
```python
1717
{! ./docs_src/quickstart/setup/block002.py !}
@@ -23,12 +23,12 @@ and run it using:
2323
fastapi dev main.py
2424
```
2525

26-
That's it. That's the main difference between setting up FastAPI and Cadwyn: you have to specify your versions. Everything you specify at app level (such as using `include_router` or `app.get(...)`) will end up unversioned and essentially function like a regular FastAPI route.
26+
That's it. That's the main difference between setting up FastAPI and Cadwyn: with Cadwyn you have to specify your API versions. Everything you specify at app level (such as using `include_router` or `app.get(...)`) will end up unversioned and essentially function like a regular FastAPI route.
2727

2828
## Docs
2929

30-
If you visit `/docs`, instead of the regular swagger, you will see a version dashboard:
30+
If you visit `/docs`, instead of the regular Swagger, you will see a version dashboard:
3131

3232
![Version dashboard](../img/unversioned_dashboard.png)
3333

34-
Clicking a card will take you to the card's regular swagger page. If you wish to see the `openapi.json` for a specific version, just use `/openapi.json?version=2000-01-01` (or whatever version you want to get).
34+
Clicking a card will take you to the card's regular Swagger page. If you wish to see the `openapi.json` for a specific version, use `/openapi.json?version=2000-01-01` (or whatever version you want to get).

docs/quickstart/tutorial.md

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,72 @@
11

22
# Tutorial
33

4-
This guide provides steps for setting up automatic API versioning using Cadwyn. I will illustrate this with an example of a User API, where we will be implementing changes to a User's address. You can also see the advanced version of the service from this tutorial [here](https://github.com/zmievsa/cadwyn/tree/main/tests/tutorial).
4+
This tutorial provides steps for setting up automatic API versioning using Cadwyn. This will be illustrated with an example of a User API with changes to a User's address. The advanced version of the service from this tutorial is available [here](https://github.com/zmievsa/cadwyn/tree/main/tests/tutorial).
55

66
Adding a new API version in Cadwyn consists of two main steps:
77

88
1. Make the breaking change
99
2. Use Cadwyn to describe how to revert the breaking change
1010

11-
In this guide, we'll prepare an environment for working with Cadwyn effectively with a basic usage example, make a breaking change, and then show how Cadwyn can help us keep the old API versions untouched.
11+
This tutorial will show how to prepare an environment for working with Cadwyn effectively in a basic usage example, how to make a breaking change, and how Cadwyn can help to keep the old API versions untouched.
1212

1313
## Step 0: Setting up
1414

15-
Let's follow the steps we discussed before for versioning using Cadwyn.
15+
Here is an initial API setup where a User has a single address. The example implements two routes - one for creating a User and the other for retrieving User details. User ID is implemented as `int` for simplicity. Please note that a `dict` is used in place of a database for simplicity but do not ever do it in real life.
1616

17-
Here is an initial API setup where the User has a single address. We will be implementing two routes - one for creating a user and another for retrieving user details. We'll be using "int" for ID for simplicity. Please note that we will use a dict in place of a database for simplicity of our examples but do not ever do it in real life.
18-
19-
The first API you come up with usually doesn't require more than one address -- why bother?
17+
The first API one comes up with usually doesn't require more than one address -- why bother?
2018

2119
```python
2220
{! ./docs_src/quickstart/tutorial/block001.py !}
2321
```
2422

25-
If we visit `/docs`, we will see the following dashboard:
23+
If you visit `/docs`, you will see the following dashboard:
2624

2725
![Dashboard with one version](../img/dashboard_with_one_version.png)
2826

29-
The app is ready to be used. Every time you would like to call its endpoints, you just need to pass `X-API-VERSION` header with value equal to `2000-01-01`. But this is just one version. Let us see what happens when we need to make a breaking change. Cadwyn supports multiple versioning styles, including header-based, path-based, and number-based.
27+
The app is ready for use. To call its endpoints, pass `X-API-VERSION` header with value equal to `2000-01-01`. But this is just one version. For a breaking change, Cadwyn supports multiple versioning styles, including header-based, path-based, and number-based.
3028

3129
## Step 1: Making the breaking change
3230

33-
During our development, we have realized that the initial API design was wrong and that addresses should have always been a list because the user wants to have multiple addresses to choose from so now we have to change the type of the "address" field to the list of strings.
31+
Assume that during the development you realize that the initial API design is wrong and that `address` should be `addresses` so that the User can have several addresses to choose from. So now you want to change the type of the `addresses` field to a list of strings.
3432

3533
```python hl_lines="2 4 15 20 30 39"
3634
{! ./docs_src/quickstart/tutorial/block002.py !}
3735
```
3836

39-
Now, every user of ours will have their API integration broken. To prevent that, we have to introduce API versioning. There aren't many methods of doing that. Most of them force you to either duplicate your schemas, your endpoints, or your entire app instance. And it makes sense, really: duplication is the only way to make sure that you will not break old versions with your new versions; the bigger the piece you duplicating -- the safer. Of course, the safest being duplicating the entire app instance and even having a separate database. But that is expensive and makes it either impossible to make breaking changes often or to support many versions. As a result, either you need infinite resources, very long development cycles, or your users will need to often migrate from version to version.
37+
Now your users will have their API integration broken. To prevent that, introduce API versioning. There aren't many methods of doing that. Most of them force you to either duplicate your schemas, your endpoints, or your entire app instance. And it makes sense, really: duplication is the only way to make sure that you will not break old versions with your new versions; the bigger the piece you are duplicating, the safer. Of course, the safest being duplicating the entire app instance and even having a separate database. But that is expensive and makes it either impossible to make breaking changes often or to support many versions. As a result, either you need infinite resources, very long development cycles, or your users need to migrate often from version to version.
4038

41-
Stripe has come up [with a solution](https://stripe.com/blog/api-versioning): let's have one HEAD app version whose responses get migrated to older versions and let's describe changes between these versions using migrations. This approach allows them to keep versions for **years** without dropping them. Obviously, each breaking change is still bad and each version still makes our system more complex and expensive, but their approach gives us a chance to minimize this complexity. Additionally, it allows us backport features and bugfixes to older versions. However, you will also be backporting bugs, which is a sad consequence of eliminating duplication.
39+
Stripe has come up [with a solution](https://stripe.com/blog/api-versioning): to have one HEAD app version whose responses get migrated to older versions and to describe changes between these versions using migrations. This approach allows Stripe to keep versions for **years** without dropping them. Obviously, each breaking change still hurts and each version still makes the system more complex and expensive, but their approach gives you a chance to minimize this complexity. Additionally, it allows you to backport features and bugfixes to older versions. However, you will also be backporting bugs, which is a sad consequence of eliminating duplication.
4240

43-
Imagine you needed to know what your code looked like two weeks ago. You would use `git checkout` or `git reset` with an older commit because `git` stores the latest version of your code (which is also called HEAD) and the differences between it and each previous version as a chain of changes. This is exactly how Stripe's approach works. They store the latest version and use the diffs to regenerate the older versions.
41+
Assume you need to know what your code looked like two weeks ago. You may use `git checkout` or `git reset` with an older commit because `git` stores the latest version of your code (which is also called HEAD) and the differences between it and each previous version as a chain of changes. This is exactly how Stripe's approach works. They store the latest version and use the diffs to regenerate the older versions.
4442

45-
Cadwyn builds upon approach so let's continue our tutorial and try to combine the two versions we created using versioning.
43+
Cadwyn is built upon this approach, so let's continue with the tutorial and combine the two versions that were created using versioning.
4644

4745
<details>
4846
<summary>Note to curious readers</summary>
4947

50-
Git doesn't actually work this way internally. My description is closer to how SVN works. It's just a really simplistic metaphor to explain a concept.
48+
Git doesn't actually work this way internally. The description above is closer to how SVN works. It's a simplistic metaphor to explain a concept.
5149
</details>
5250

5351
## Step 2: Describing how to revert the breaking change
5452

55-
To fix the old integrations of our clients, we need to add back the `2000-01-01` version and its state. In Cadwyn, it is done using version changes (or, as Stripe calls them, version gates). You could also think of them as reverse database migrations (database migrations to downgrade to the previous state of the database). Essentially version changes describe the difference between the latest version and the previous version. They are a way to say "Okay, we have applied the breaking changes but here is how we would revert these changes for our old clients".
53+
To fix the old integrations of your clients, you need to add back the `2000-01-01` version and its state. In Cadwyn, it is done using version changes (or, as Stripe calls them, version gates). You could also think of them as reverse database migrations (database migrations to downgrade to the previous state of the database). Essentially version changes describe the difference between the latest version and the previous version. They are a way to say "Okay, we have applied the breaking changes but here is how we would revert these changes for our old clients".
5654

57-
For every endpoint whose `response_model` is `UserResource`, this migration will convert the list of addresses back to a single address when migrating to the previous version. Our goal is to have an app of [HEAD version](../concepts/version_changes.md#headversion) and to describe what older versions looked like in comparison to it. That way the old versions are frozen in migrations and you can **almost** safely forget about them.
55+
For every endpoint whose `response_model` is `UserResource`, this migration will convert the list of addresses back to a single address when migrating to the previous version. Your goal is to have an app of [HEAD version](../concepts/version_changes.md#headversion) and to describe what older versions looked like in comparison to it. That way the old versions are frozen in migrations and you can **almost** safely forget about them.
5856

5957
```python hl_lines="8-9 12 14-16 45-66"
6058
{! ./docs_src/quickstart/tutorial/block003.py !}
6159
```
6260

63-
See how we are popping the first address from the list? This is only guaranteed to be possible because we specified earlier that `min_length` for `addresses` must be `1`. If we didn't, then the user would be able to create a user in a newer version that would be impossible to represent in the older version. I.e. If anyone tried to get that user from the older version, they would get a `ResponseValidationError` because the user wouldn't have data for a mandatory `address` field. You need to always keep in mind that API versioning is only for versioning your **API**, your interface. Your versions must still be completely compatible in terms of data. If they are not, then you are versioning your data and you should really go with a separate app instance. Otherwise, your users will have a hard time migrating back and forth between API versions and so many unexpected errors.
61+
See how the first address is popped from the list? This is guaranteed to be possible if you specified earlier that `min_length` for `addresses` must be `1`. If you didn't, then a user would be able to create a User in a newer version that would be impossible to represent in the older version. I.e. if anyone tried to get that user from the older version, they would get a `ResponseValidationError` because the user wouldn't have data for a mandatory `address` field. You need to always keep in mind that API versioning is only for versioning your **API**, your interface. Your versions must still be completely compatible in terms of data. If they are not, then you are versioning your data and you should go with a separate app instance. Otherwise, your users will have a hard time migrating back and forth between API versions and many unexpected errors.
6462

65-
See how we added a migration not only for response but also for request? This will allow our business logic to stay completely the same, no matter which version it was called from. Cadwyn will always give your business logic the request model from the HEAD version by wrapping each request in it.
63+
Also note that the migration was added not only for the response but also for the request. This will allow your business logic to stay completely the same, no matter which version it was called from. Cadwyn will always give your business logic the request model from the HEAD version by wrapping each request in it.
6664

67-
Let's run our app and take a look at the generated dashboard and OpenAPI schemas:
65+
Run the app and take a look at the generated dashboard and OpenAPI schemas:
6866

6967
![Dashboard with two versions](../img/dashboard_with_two_versions.png)
7068
![GET /users/{user_id} endpoint in OpenAPI](../img/get_users_endpoint_from_prior_version.png)
7169

72-
The endpoint above is from the `2000-01-01` version. As you see, our routes and business logic are for the HEAD version but our OpenAPI has all information about all API versions which is the main goal of Cadwyn: a large number of long-living API versions without placing any burden on your business logic.
70+
The endpoint above is from the `2000-01-01` version. As you see, your routes and business logic are for the HEAD version but your OpenAPI has all the information about all the API versions which is the main goal of Cadwyn: a large number of long-living API versions without placing any burden on your business logic.
7371

74-
Obviously, this was just a simple example and cadwyn has a lot more features so if you're interested -- take a look at the [how-to](../how_to/index.md) and [concepts](../concepts/index.md) sections.
72+
Obviously, this was just a simple example and Cadwyn has a lot more features so if you're interested -- take a look at the [how-to](../how_to/index.md) and [concepts](../concepts/index.md) sections.

0 commit comments

Comments
 (0)