Preface |
|
xvi | |
Acknowledgments |
|
xviii | |
About this book |
|
xx | |
About the author |
|
xxv | |
About the cover illustration |
|
xxvi | |
|
PART 1 Introducing Microservice APIs |
|
|
1 | (58) |
|
1 What are microservice APIs? |
|
|
3 | (17) |
|
1.1 What are microservices? |
|
|
4 | (4) |
|
|
4 | (1) |
|
Microservices vs. monoliths |
|
|
5 | (2) |
|
Microservices today and how we got here |
|
|
7 | (1) |
|
|
8 | (3) |
|
|
8 | (1) |
|
|
9 | (1) |
|
How do APIs help us drive microservices integrations? |
|
|
9 | (2) |
|
1.3 Challenges of microservices architecture |
|
|
11 | (4) |
|
Effective service decomposition |
|
|
11 | (1) |
|
Microservices integration tests |
|
|
12 | (1) |
|
Handling service unavailability 12' Tracing distributed transactions |
|
|
13 | (1) |
|
Increased operational complexity and infrastructure overhead |
|
|
14 | (1) |
|
1.4 Introducing documentation-driven development |
|
|
15 | (2) |
|
1.5 Introducing the CoffeeMesh application |
|
|
17 | (1) |
|
1.6 Who this book is for and what you will learn |
|
|
17 | (3) |
|
2 A. basic API implementation |
|
|
20 | (25) |
|
2.1 Introducing the orders API specification |
|
|
21 | (1) |
|
2.2 High-level architecture of the orders application |
|
|
22 | (1) |
|
2.3 Implementing the API endpoints |
|
|
23 | (7) |
|
2.4 Implementing data validation models with pydantic |
|
|
30 | (4) |
|
2.5 Validating request payloads with pydantic |
|
|
34 | (4) |
|
2.6 Marshalling and validating response payloads with pydantic |
|
|
38 | (3) |
|
2.7 Adding an in-memory list of orders to the API |
|
|
41 | (4) |
|
3 Designing microservices |
|
|
45 | (14) |
|
3.1 Introducing CoffeeMesh |
|
|
46 | (1) |
|
3.2 Microservices design principles |
|
|
46 | (3) |
|
Database-per-service principle |
|
|
46 | (2) |
|
|
48 | (1) |
|
Single Responsibility Principle |
|
|
49 | (1) |
|
3.3 Service decomposition by business capability |
|
|
49 | (3) |
|
Analyzing the business structure of CoffeeMesh |
|
|
49 | (1) |
|
Decomposing microservices by business capabilities |
|
|
50 | (2) |
|
3.4 Service decomposition by subdomains |
|
|
52 | (5) |
|
What is domain-driven design? |
|
|
52 | (1) |
|
Applying strategic analysis to CoffeeMesh |
|
|
53 | (4) |
|
3.5 Decomposition by business capability vs. decomposition by subdomain |
|
|
57 | (2) |
|
PART 2 Designing and building REST APIs |
|
|
59 | (124) |
|
4 Principles of REST API design |
|
|
61 | (29) |
|
|
62 | (1) |
|
4.2 Architectural constraints of REST applications |
|
|
63 | (4) |
|
Separation of concerns: The client-server architecture principle |
|
|
64 | (1) |
|
Make it scalable: The statelessness principle |
|
|
64 | (1) |
|
Optimize for performance: The cacheability principle |
|
|
65 | (1) |
|
Make it simple for the client: The layered system principle Extendable interfaces: The code-on-demand principle |
|
|
66 | (1) |
|
Keep it consistent: The uniform interface principle |
|
|
67 | (1) |
|
4.3 Hypermedia as the engine of application state |
|
|
67 | (3) |
|
4.4 Analyzing the maturity of an API with the Richardson maturity model |
|
|
70 | (3) |
|
Level 0 Web APIs a la RPC |
|
|
71 | (1) |
|
Level 1 Introducing the concept of resource |
|
|
71 | (1) |
|
Level 2 Using HTTP methods and status codes |
|
|
72 | (1) |
|
Level 3 API discoverability |
|
|
72 | (1) |
|
4.5 Structured resource URLs with HTTP methods |
|
|
73 | (4) |
|
4.6 Using HTTP status codes to create expressive HTTP responses |
|
|
77 | (6) |
|
What are HTTP status codes? |
|
|
77 | (1) |
|
Using HTTP status codes to report client errors in the request |
|
|
78 | (4) |
|
Using HTTP status codes to report errors in the server |
|
|
82 | (1) |
|
4.7 Designing API payloads |
|
|
83 | (4) |
|
What are HTTP payloads, and when do we use them? |
|
|
83 | (1) |
|
HTTP payload design patterns |
|
|
84 | (3) |
|
4.8 Designing URL query parameters |
|
|
87 | (3) |
|
5 Documenting REST APIs with OpenAPI |
|
|
90 | (20) |
|
5.1 UsingJSON Schema to model data |
|
|
91 | (4) |
|
5.2 Anatomy of an OpenAPI specification |
|
|
95 | (1) |
|
5.3 Documenting the API endpoints |
|
|
96 | (1) |
|
5.4 Documenting URL query parameters |
|
|
97 | (1) |
|
5.5 Documenting request payloads |
|
|
98 | (2) |
|
5.6 Refactoring schema definitions to avoid repetition |
|
|
100 | (2) |
|
5.7 Documenting API responses |
|
|
102 | (3) |
|
5.8 Creating generic responses |
|
|
105 | (2) |
|
5.9 Defining the authentication scheme of the API |
|
|
107 | (3) |
|
6 Building REST APIs with Python |
|
|
110 | (34) |
|
6.1 Overview of the orders API |
|
|
111 | (1) |
|
6.2 URL query parameters for the orders API |
|
|
112 | (3) |
|
6.3 Validating payloads with unknown fields |
|
|
115 | (3) |
|
6.4 Overriding FastAPI's dynamically generated specification |
|
|
118 | (2) |
|
6.5 Overview of the kitchen API |
|
|
120 | (2) |
|
6.6 Introducing flask-smorest |
|
|
122 | (1) |
|
6.7 Initializing the web application for the API |
|
|
123 | (2) |
|
6.8 Implementing the API endpoints |
|
|
125 | (4) |
|
6.9 Implementing payload validation models with marshmallow |
|
|
129 | (4) |
|
6.10 Validating URL query parameters |
|
|
133 | (3) |
|
6.11 Validating data before serializing the response |
|
|
136 | (4) |
|
6.12 Implementing an in-memory list of schedules |
|
|
140 | (2) |
|
6.13 Overriding flask-smorest's dynamically generated API specification |
|
|
142 | (2) |
|
7 Service implementation patterns for microservices |
|
|
144 | (39) |
|
7.1 Hexagonal architectures for microservices |
|
|
145 | (3) |
|
7.2 Setting up the environment and the project structure |
|
|
148 | (1) |
|
7.3 Implementing the database models |
|
|
149 | (6) |
|
7.4 Implementing the repository pattern for data access |
|
|
155 | (7) |
|
The case for the repository pattern: What is it, and why is it useful? |
|
|
155 | (2) |
|
Implementing the repository pattern |
|
|
157 | (5) |
|
7.5 Implementing the business layer |
|
|
162 | (10) |
|
7.6 Implementing the unit of work pattern |
|
|
172 | (5) |
|
7.7 Integrating the API layer and the service layer |
|
|
177 | (6) |
|
PART 3 Designing and building GraphQL APIs |
|
|
183 | (84) |
|
|
185 | (25) |
|
|
186 | (3) |
|
8.2 Introducing the products API |
|
|
189 | (3) |
|
8.3 Introducing GraphQL's type system |
|
|
192 | (3) |
|
Creating property definitions with scalars |
|
|
192 | (1) |
|
Modeling resources with object types |
|
|
193 | (1) |
|
|
194 | (1) |
|
8.4 Representing collections of items with lists |
|
|
195 | (1) |
|
8.5 Think graphs: Building meaningful connections between object types |
|
|
196 | (4) |
|
Connecting types through edge properties |
|
|
196 | (2) |
|
Creating connections with through types |
|
|
198 | (2) |
|
8.6 Combining different types through unions and interfaces |
|
|
200 | (2) |
|
8.7 Constraining property values with enumerations |
|
|
202 | (1) |
|
8.8 Defining queries to serve data from the API |
|
|
203 | (3) |
|
8.9 Altering the state of the server with mutations |
|
|
206 | (4) |
|
|
210 | (23) |
|
9.1 Running a GraphQL mock server |
|
|
211 | (3) |
|
9.2 Introducing GraphQL queries |
|
|
214 | (3) |
|
|
214 | (1) |
|
Running queries with parameters |
|
|
215 | (1) |
|
Understanding query errors |
|
|
215 | (2) |
|
9.3 Using fragments in queries |
|
|
217 | (2) |
|
9.4 Running queries with input parameters |
|
|
219 | (1) |
|
9.5 Navigating the API graph |
|
|
219 | (2) |
|
9.6 Running multiple queries and query aliasing |
|
|
221 | (4) |
|
Running multiple queries in the same request |
|
|
221 | (1) |
|
|
222 | (3) |
|
9.7 Running GraphQL mutations |
|
|
225 | (1) |
|
9.8 Running parameterized queries and mutations |
|
|
226 | (3) |
|
9.9 Demystifying GraphQL queries |
|
|
229 | (1) |
|
9.10 Calling a GraphQL API with Python code |
|
|
230 | (3) |
|
10 Building GraphQL APIs with Python |
|
|
233 | (34) |
|
10.1 Analyzing the API requirements |
|
|
234 | (1) |
|
10.2 Introducing the tech stack |
|
|
234 | (1) |
|
|
235 | (6) |
|
10.4 Implementing the products API |
|
|
241 | (26) |
|
Laying out the project structure |
|
|
241 | (1) |
|
Creating an entry point for the GraphQL server |
|
|
242 | (1) |
|
Implementing query resolvers |
|
|
243 | (3) |
|
Implementing type resolvers |
|
|
246 | (6) |
|
Handling query parameters |
|
|
252 | (4) |
|
Implementing mutation resolvers |
|
|
256 | (2) |
|
Building resolvers for custom scalar types |
|
|
258 | (4) |
|
Implementing field resolvers |
|
|
262 | (5) |
|
PART 4 Securing, testing, and deploying microservtce apis |
|
|
267 | (109) |
|
API authorization and authentication |
|
|
269 | (1) |
|
11.1 Setting up the environment for this chapter |
|
|
270 | (1) |
|
11.2 Understanding authentication and authorization protocols |
|
|
271 | (7) |
|
Understanding Open Authorization |
|
|
271 | (5) |
|
Understanding OpenID Connect |
|
|
276 | (2) |
|
11.3 Working with JSON Web Tokens |
|
|
278 | (9) |
|
Understanding the JWT header |
|
|
279 | (1) |
|
|
280 | (2) |
|
|
282 | (2) |
|
|
284 | (2) |
|
|
286 | (1) |
|
11.4 Adding authorization to the API server |
|
|
287 | (6) |
|
Creating an authorization module |
|
|
287 | (2) |
|
Creating an authorization middleware |
|
|
289 | (3) |
|
|
292 | (1) |
|
11.5 Authorizing resource access |
|
|
293 | (9) |
|
Updating the database to link users and orders |
|
|
294 | (3) |
|
Restricting user access to their own resources |
|
|
297 | (5) |
|
12 Testing and validating APIs |
|
|
302 | (29) |
|
12.1 Setting up the environment for API testing |
|
|
303 | (1) |
|
12.2 Testing REST APIs with Dredd |
|
|
304 | (11) |
|
|
304 | (1) |
|
Installing and running Dredd's default test suite |
|
|
305 | (2) |
|
Customizing Dredd's test suite with hooks |
|
|
307 | (8) |
|
Using Dredd in your API testing strategy |
|
|
315 | (1) |
|
12.3 Introduction to property-based testing |
|
|
315 | (7) |
|
What is property-based testing? |
|
|
315 | (1) |
|
The traditional approach to API testing |
|
|
316 | (2) |
|
Property-based testing with Hypothesis |
|
|
318 | (1) |
|
Using Hypothesis to test a REST API endpoint |
|
|
319 | (3) |
|
12.4 Testing REST APIs with Schemathesis |
|
|
322 | (5) |
|
Running Schemathesis's default test suite |
|
|
322 | (1) |
|
Using links to enhance Schemathesis' test suite |
|
|
323 | (4) |
|
12.5 Testing GraphQL APIs |
|
|
327 | (2) |
|
Testing GraphQL APIs with Schemathesis |
|
|
327 | (2) |
|
12.6 Designing your API testing strategy |
|
|
329 | (2) |
|
13 Dockerizing microservice APIs |
|
|
331 | (11) |
|
13.1 Setting up the environment for this chapter |
|
|
332 | (1) |
|
13.2 Dockerizing a microservice |
|
|
333 | (5) |
|
13.3 Running applications with Docker Compose |
|
|
338 | (2) |
|
13.4 Publishing Docker builds to a container registry |
|
|
340 | (2) |
|
14 Deploying microservice APIs with Kubemetes |
|
|
342 | (34) |
|
14.1 Setting up the environment for this chapter |
|
|
343 | (1) |
|
14.2 How Kubernetes works: The "CliffsNotes" version |
|
|
344 | (2) |
|
14.3 Creating a Kubernetes cluster with EKS |
|
|
346 | (4) |
|
14.4 Using IAM roles for Kubernetes service accounts |
|
|
350 | (1) |
|
14.5 Deploying a Kubernetes load balancer |
|
|
351 | (2) |
|
14.6 Deploying microservices to the Kubernetes cluster |
|
|
353 | (8) |
|
Creating a deployment object |
|
|
354 | (3) |
|
Creating a service object |
|
|
357 | (2) |
|
Exposing services with ingress objects |
|
|
359 | (2) |
|
14.7 Setting up a serverless database with AWS Aurora |
|
|
361 | (9) |
|
Creating an Aurora Serverless database |
|
|
361 | (3) |
|
Managing secrets in Kubernetes |
|
|
364 | (3) |
|
Running the database migrations and connecting our service to the database |
|
|
367 | (3) |
|
14.8 Updating the OpenAPI specification with the ALB's hostname |
|
|
370 | (2) |
|
14.9 Deleting the Kubernetes cluster |
|
|
372 | (4) |
Appendix A Types of web APIs and protocols |
|
376 | (11) |
Appendix B Managing an API's life cycle |
|
387 | (4) |
Appendix C API authorization using an identity provider |
|
391 | (12) |
Index |
|
403 | |