<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: José Haro Peralta</title>
    <description>The latest articles on DEV Community by José Haro Peralta (@abunuwas).</description>
    <link>https://dev.to/abunuwas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F789075%2Ff2c254d5-5c75-4ea7-9d76-d1fd499b7255.png</url>
      <title>DEV Community: José Haro Peralta</title>
      <link>https://dev.to/abunuwas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abunuwas"/>
    <language>en</language>
    <item>
      <title>Faster time-to-market with API-first</title>
      <dc:creator>José Haro Peralta</dc:creator>
      <pubDate>Tue, 25 Oct 2022 23:10:36 +0000</pubDate>
      <link>https://dev.to/abunuwas/faster-time-to-market-with-api-first-3hhd</link>
      <guid>https://dev.to/abunuwas/faster-time-to-market-with-api-first-3hhd</guid>
      <description>&lt;p&gt;A few years ago, I had the opportunity to work in a short engagement with a London startup helping them to build an MVP for one of their latest products. They wanted to trial a strategic partnership with a bigger company within the same sector. The startup would provide a solution for an untapped niche of the market, while the bigger company would act as a catalyst for customers who trust an established brand.&lt;/p&gt;

&lt;p&gt;The idea for the MVP was very simple: we had to build a small UI using an SPA framework, and a web service exposing a REST API that would allow both integration with the SPA and with the strategic business partner.&lt;/p&gt;

&lt;p&gt;Despite the simplicity of the idea, and although the project had started just a few weeks before I joined, some parts of the project were already a hot mess. In particular around the API.&lt;/p&gt;

&lt;p&gt;For starters, we didn’t have actual API documentation anywhere. We had a Google Doc with some notes about the endpoints, and a few examples of the payloads we could expect. The problem was the documentation was very incomplete, half of it was wrong, and it wasn’t maintained. &lt;/p&gt;

&lt;p&gt;Needless to say, the API implementation wasn’t tested against the contract, since there was no contract at all! Hence the backend developers were able to release API changes without notice or visibility. Unfortunately, many of these changes weren’t really intended - they were bugs. And sadly, they often broke the integration with the API client. It was frustrating both for the UI developers and for the backend developers.&lt;/p&gt;

&lt;p&gt;The second major problem was the backend team wasn’t using a proper API framework. They used plain &lt;a href="https://github.com/pallets/flask"&gt;Flask&lt;/a&gt; with custom payload validation which they themselves implemented. You can picture it: hundreds of lines of code dedicated to API validation (for dates, for timestamps, for string formats, and so on), most of them untested. No wonder the project was going slow.&lt;/p&gt;

&lt;p&gt;To get the project back on track, we had to fix the API documentation, get a proper API framework in place, and make sure no releases were allowed if they didn’t comply with the API specification. Let’s see how we tackled each of these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the API design and docs
&lt;/h2&gt;

&lt;p&gt;It turned out the team just wasn’t aware of REST APIs best practices and standards and didn’t know about the &lt;a href="https://openapis.org/"&gt;OpenAPI&lt;/a&gt; specification. So the first thing I did was to explain what OpenAPI is and how it works. Then we consolidated the API documentation in an OpenAPI specification. This allowed us to be very clear about what to expect from the API.&lt;/p&gt;

&lt;p&gt;The process of consolidating the documentation into an OpenAPI specification also brought to light plenty of problems with the previous API design. For example, dates were being represented with a custom format that required custom validation logic both in the server and in the UI. We replaced those dates with ISO standards, which are &lt;a href="https://spec.openapis.org/oas/v3.0.1#data-types"&gt;supported by OpenAPI&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Some schemas also proved to be too flexible and therefore barely useful for validation. In fact, under the name of reusability, the same schemas were reused for different entities, resulting in very &lt;strong&gt;complicated schemas&lt;/strong&gt; in which &lt;strong&gt;most properties&lt;/strong&gt; were &lt;strong&gt;optional&lt;/strong&gt;. To improve validation, we refactored the schemas and created a &lt;strong&gt;one model per entity&lt;/strong&gt;, with different endpoints.&lt;/p&gt;

&lt;p&gt;Another limitation of the previous design was making &lt;strong&gt;inappropriate use of HTTP methods and status codes&lt;/strong&gt;. The only HTTP methods they used were &lt;strong&gt;GET&lt;/strong&gt; and &lt;strong&gt;POST&lt;/strong&gt;, and all the responses returned &lt;strong&gt;200 status codes&lt;/strong&gt;. This wasn’t very useful. The improper use of HTTP methods meant it wasn’t clear when we were trying to create, delete, or update a resource. And since all the responses returned 200, it appeared they were all successful and you had to inspect the actual payload in search for an “error” property to determine whether the request succeeded or not.&lt;/p&gt;

&lt;p&gt;From a developer point of view, it may seem like a good idea to “reuse” endpoints. For example, if you capture create, update, and delete operations through a POST endpoint, you’ll have less endpoints to maintain. And if all those endpoints have similar blocks of code, you may also save a few lines. However, you can also do all these things while exposing different endpoints for each operation. This is a common mistake I often encounter among API developers: we &lt;strong&gt;expose our implementation details through the API interface&lt;/strong&gt;, and that happens because we think of the &lt;strong&gt;code first&lt;/strong&gt;. Instead, I encourage you to &lt;strong&gt;design your API first, and think about the implementation later&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consolidating the API specification with OpenAPI was a turning point for the project. From that moment we were able to run &lt;strong&gt;mock servers&lt;/strong&gt; to build and test the UI before integrating with the backend, and we were able to &lt;strong&gt;validate the backend&lt;/strong&gt; implementation against the specification. We used &lt;strong&gt;&lt;a href="https://github.com/stoplightio/prism"&gt;prism&lt;/a&gt;&lt;/strong&gt; to run mock servers, and &lt;a href="https://github.com/apiaryio/dredd"&gt;&lt;strong&gt;Dredd&lt;/strong&gt;&lt;/a&gt; to validate the server implementation (these days I’d rather use &lt;a href="https://github.com/schemathesis/schemathesis"&gt;&lt;strong&gt;schemathesis&lt;/strong&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the API release process
&lt;/h2&gt;

&lt;p&gt;Having API documentation is very good, but without testing the implementation against the API specification before a release, it’s not much use really. Sure it helps us get a clear understanding of how the API works. But the power of API documentation is serving as a &lt;strong&gt;validation tool&lt;/strong&gt; - it helps us verify that the server is correctly implemented.&lt;/p&gt;

&lt;p&gt;To ensure the API server worked as expected, I included the &lt;strong&gt;Dredd test suite&lt;/strong&gt; in the &lt;strong&gt;Continuous Integration&lt;/strong&gt; server, so nobody would be able to merge and release new code unless it was validated by Dredd and therefore compliant with the API specification. Thanks to this, we &lt;strong&gt;stopped having silent changes&lt;/strong&gt; to the API server. From that moment on, any changes to the server would have to be documented first, and the API server would have to comply with the changes before merging or releasing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the server implementation
&lt;/h2&gt;

&lt;p&gt;The API server was implemented with &lt;a href="https://github.com/pallets/flask"&gt;Flask&lt;/a&gt;, a popular Python framework for building web applications. Before I joined the team, they’d been using &lt;strong&gt;plain Flask&lt;/strong&gt; to build the API, and they wrote a lot of &lt;strong&gt;custom code&lt;/strong&gt; to validate API payloads. This is a common mistake I see often among less experienced API developers.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong - building your own API validation layer isn’t necessarily bad, but if you go down this route, you’ll end up &lt;strong&gt;reinventing the wheel&lt;/strong&gt;. APIs require complex validation logic for both payloads and URLs (path and query parameters), and this logic has to be applied throughout the API. So if you build your own API validation layer, you’ll end up creating an API framework. The thing is, there’re tons of API development frameworks out there, and very good ones, so why not use one of them?&lt;/p&gt;

&lt;p&gt;When it comes to Flask, in particular, there’re plenty of choices. And in fairness, not all frameworks are created equal. You’ve got &lt;a href="https://github.com/flasgger/flasgger"&gt;flasgger&lt;/a&gt;, &lt;a href="https://github.com/python-restx/flask-restx"&gt;restx&lt;/a&gt; (successor of &lt;a href="https://github.com/noirbizarre/flask-restplus"&gt;flask-restplus&lt;/a&gt;), &lt;a href="https://github.com/flask-restful/flask-restful"&gt;flask-RESTful&lt;/a&gt;, and &lt;a href="https://github.com/marshmallow-code/flask-smorest"&gt;flask-smorest&lt;/a&gt;, to mention a few. How do you choose among those???&lt;/p&gt;

&lt;p&gt;When choosing a REST API development framework, you’re looking mainly for the following factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It supports OpenAPI out of the box&lt;/strong&gt;: if you’re going to build a REST API, you need to use a framework that knows how OpenAPI works. Otherwise, you’ll get nasty surprises when it comes to payload validation. The easiest way to determine if a framework supports OpenAPI is by checking whether it can auto generate API documentation from your code. Flasgger and flask-smorest both do this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uses a robust data validation library&lt;/strong&gt;: validating payloads is a complex business. Your data validation library must handle optional and required properties, string formats like ISO dates and UUIDs (both dates and UUIDs are string types in OpenAPI), and strict vs loose type validation (should a string pass as an integer if it can be casted?). Also, in the case of Python, you need to make sure 1 and 0 don’t pass for True and False when it comes to boolean properties. In my experience, the best data validation libraries in the Python ecosystem are &lt;a href="https://github.com/pydantic/pydantic"&gt;pydantic&lt;/a&gt; and &lt;a href="https://github.com/marshmallow-code/marshmallow"&gt;marshmallow&lt;/a&gt;. From the above-mentioned libraries, flasgger and flask-smorest work with marshmallow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It validates everything&lt;/strong&gt;: It validates request payloads, response payloads, URL path parameters, and URL query parameters. I’ve noticed some libraries only validate request payloads and provide very little support for validating URL parameters or response payloads. In those cases, make sure at least the library gives you a way to enforce your own validation rules for responses and URL parameters. If your library uses marshmallow, you can always use the marshmallow model directly to validate a response payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maturity&lt;/strong&gt;: if your business (or your job) depends on the APIs you’re building, you want to build them with robust and mature libraries. Look for libraries that have been around for a while and with lots of users and contributors. Look for libraries with an active community, in which users raise issues often, but check that the issues are quickly addressed. It also helps if the library has good documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After applying this analysis, we chose to work with flask-smorest, which is a Flask plugin that allows you to easily build REST APIs using marshmallow for data validation. This not only allowed us to remove hundreds of lines of custom data validation code - it also improved data validation. Both request and response payloads were now properly validated. Also, while URL query or path parameters were not being validated before, now marshmallow was taking care of all that.&lt;/p&gt;

&lt;p&gt;Using a proper API framework was a game changer - it gave us a properly working API. Because the framework takes care of everything in the API layer, we were able to spend less time working on the API layer, and focus our efforts on the business layer and the application’s data model. Our development speed got faster and we were able to release better software more often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moral of the story
&lt;/h2&gt;

&lt;p&gt;APIs are deceivingly simple. At the end of the day, APIs are everywhere and we all use APIs all the time. However, in reality APIs are complex pieces of software. Anyone can build a simple API, but then anyone can also write some random code. Just like the most challenging thing in software development is writing readable and maintainable code, the most challenging thing in API development is delivering good interfaces that work as expected, are easy to consume, and easy to change.&lt;/p&gt;

&lt;p&gt;Delivering good APIs is difficult because it requires &lt;strong&gt;alignment with the business&lt;/strong&gt;: our API must meet the requirements of our organization. It requires &lt;strong&gt;alignment between the client- and the server-sides&lt;/strong&gt; of the API: they must work together and use the same contract, otherwise the integration won’t work. And we must ensure that both the client and the server are &lt;strong&gt;implemented according to the specification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Many skills are needed to make this happen: you need to know how to engage with the stakeholders and gather requirements, how to translate those requirements into technical details, how to design an API, how to document it, how to choose a good API framework and how to use it correctly, how to test the API, and how to ensure the implementation follows the design. And this isn’t even considering API security, deployments, and operations. Those too are extremely complex topics, but that’s for another day.&lt;/p&gt;

&lt;p&gt;If you’re building business-critical APIs, my recommendation is don’t leave it to your most junior developers on their own. By all means engage junior developers, but make sure their work is supervised by a senior developer who knows how to build APIs. If you don’t have previous experience building APIs, &lt;strong&gt;stick to best practices&lt;/strong&gt;: design the API first, document it, build according to the spec, and validate it against the spec. And don’t reinvent the wheel - use proper frameworks!&lt;/p&gt;

&lt;p&gt;I know it sounds like a lot of work: you need to learn OpenAPI, figure out how to use API testing frameworks like Dredd and schemathesis, research API development frameworks, and so on. But it’s worth the effort. The alternative is a &lt;strong&gt;downward spiral&lt;/strong&gt; of API integrations and software quality problems that will hold your progress back and will cost your business a lot of money to fix.&lt;/p&gt;

&lt;p&gt;There’re a lot of great resources to help you get started with best API development practices. You may want to check out the works of &lt;a href="https://twitter.com/kinlane"&gt;Kin Lane&lt;/a&gt;, &lt;a href="https://twitter.com/arno_di_loreto"&gt;Arnaud Lauret&lt;/a&gt;, &lt;a href="https://twitter.com/mamund"&gt;Mike Amundsen&lt;/a&gt;, &lt;a href="https://twitter.com/dret"&gt;Erik Wilde&lt;/a&gt;, &lt;a href="https://twitter.com/medjawii"&gt;Mehdi Medjaoui&lt;/a&gt;, &lt;a href="https://twitter.com/mitraman"&gt;Ronnie Mitra&lt;/a&gt;, &lt;a href="https://twitter.com/launchany"&gt;James Higginbotham&lt;/a&gt;, &lt;a href="https://twitter.com/hAPI_hacker"&gt;Corey J. Ball&lt;/a&gt;, &lt;a href="https://twitter.com/jgeewax"&gt;JJ Geewax&lt;/a&gt;, and &lt;a href="https://twitter.com/JoseHaroPeralta"&gt;yours truly&lt;/a&gt;, among others. Don’t say you couldn’t find any resources!&lt;/p&gt;

&lt;p&gt;And if you need additional help, reach out to the experts directly (check previous paragraph)! Based on my own observations working with different clients and watching many API disasters, I estimate that companies without adequate talent or experience waste somewhere between $50k and $500k trying to get their APIs right. Sometimes it’s way more than that. But money isn’t the biggest problem here: it’s the legacy code that gets built along the way, the burnout among the employees (who often end up leaving the company), and the loss of business opportunities, either because the project ships late, or because it gets canceled altogether. So even if you have to pay $50k for a consulting/training session to get things right, you’ll still be much better off: you’ll have saved time and money by getting yourself on the right track from the beginning.&lt;/p&gt;




&lt;p&gt;If you enjoyed reading this article and found it useful, you’ll like my book &lt;a href="https://www.manning.com/books/microservice-apis"&gt;&lt;strong&gt;Microservice APIs&lt;/strong&gt;&lt;/a&gt;. It teaches you everything you need to know to build APIs from the ground up, including design, documentation, implementation, testing, security, and deployments!&lt;/p&gt;

&lt;p&gt;You can download &lt;strong&gt;two chapters&lt;/strong&gt; of the book for &lt;strong&gt;free&lt;/strong&gt; from this &lt;a href="https://www.microapis.io/resources/microservice-apis-in-python"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also use the following code to obtain a &lt;strong&gt;40% discount&lt;/strong&gt; when buying the book: &lt;strong&gt;slperalta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I also run a very popular series of &lt;strong&gt;workshops&lt;/strong&gt;, both free and paid ones, in which I teach best practices for building microservices and APIs. I’d love to see you around in one of my workshops! An updated list of upcoming workshops is always available here: &lt;a href="https://microapis.io/workshops"&gt;https://microapis.io/workshops&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
      <category>microservices</category>
      <category>webdev</category>
    </item>
    <item>
      <title>API-first development maturity framework</title>
      <dc:creator>José Haro Peralta</dc:creator>
      <pubDate>Tue, 06 Sep 2022 14:29:29 +0000</pubDate>
      <link>https://dev.to/abunuwas/api-first-development-maturity-framework-2k69</link>
      <guid>https://dev.to/abunuwas/api-first-development-maturity-framework-2k69</guid>
      <description>&lt;p&gt;In conversations with software developers, I’ve noticed that most of them claim to be API-first in their API development strategy. In fact, &lt;a href="https://www.postman.com/state-of-api/api-first-strategies/#api-first-strategies"&gt;Postman’s 2021 State of API Report&lt;/a&gt; found that 67% of respondents ranked themselves with a score of 5 or higher (on a scale of 0 to 10) in terms of embracing API-first development.&lt;/p&gt;

&lt;p&gt;In the past years, I’ve helped many teams build and deliver APIs. Most of them claimed to be API-first. But as it turned out, being API-first took on a different meaning in every project. This isn’t surprising. Postman’s 2021 State of the API Report includes the following definitions of API-first (with the percentage of respondents that subscribed them in parentheses):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Defining and designing APIs and schema before beginning development” (42% or respondents)&lt;/li&gt;
&lt;li&gt;“Developing APIs before developing applications or integrations” (31% of respondents)&lt;/li&gt;
&lt;li&gt;“Defining business requirements before defining and designing APIs” (16% of respondents)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remaining 10% of respondents admitted they don’t know what API-first means.&lt;/p&gt;

&lt;p&gt;Despite the diversity of perceptions around what API-first means, over the years I’ve noticed an increasing consolidation of the concept. When I’ve had the opportunity to work with the same team for a long period of time, I’ve noticed that &lt;strong&gt;over time the definition of API-first becomes more clear&lt;/strong&gt; and solid. And as the concept of API-first matures, I also notice how API development methods become more sophisticated and standardized. In view of this, I’d like to propose &lt;strong&gt;a framework for analyzing API-first development strategies&lt;/strong&gt;. I call it the &lt;strong&gt;API-first maturity framework&lt;/strong&gt; (yes, inspired by Leonard Richardson).&lt;/p&gt;

&lt;p&gt;I want to make it clear that this is a framework for analyzing API-first &lt;strong&gt;development&lt;/strong&gt; strategies. I don’t analyze API-first strategies in general, but focus specifically on API-first development: &lt;strong&gt;the strategy you use to build your APIs&lt;/strong&gt;. Since we’re talking API-first, the framework assumes that &lt;strong&gt;there’s always an element of API design before heading on to the implementation&lt;/strong&gt;. However there’re no assumptions about how you design the API or how and when you document it. In fact, this framework considers different strategies for documenting APIs and evaluates them in terms of their benefits and constraints. The discussion also focuses mainly on REST since it’s the most prevalent type of API, but the same conclusions in my experience apply to GraphQL and other types of APIs.&lt;/p&gt;

&lt;p&gt;In the early days of API development, very few people had a clear idea of how to design and document their APIs, and the API development workflow was full of hiccups. I used to say that &lt;strong&gt;no API server survives first contact with its client&lt;/strong&gt;. And to an extent, this is still true even for the most advanced API development workflows. However, the benefit of a robust API development workflow is not to eliminate all integration errors, but to &lt;strong&gt;give you more control and observability of the errors and enable you to fix them more quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ideal is being able to deliver API integrations that work regardless of the complexity of the API and the integration&lt;/strong&gt;. This means that both the API client and the API server development teams have a crystal &lt;strong&gt;clear idea of how the API works&lt;/strong&gt;, and that both the API client and the server are &lt;strong&gt;implemented and validated against the API design&lt;/strong&gt;. I assess the maturity of each API documentation methodology and each API development workflow in terms of their ability to help us reach this ideal.&lt;/p&gt;

&lt;p&gt;I divide the analysis into two categories: 1) API modeling and documentation strategies and 2) API development workflows. For each category, I’ll discuss the most common strategies and workflows that I’ve found when working with different teams. Before heading over to the analysis, here’s the list of documentation strategies and development workflows that I’ll analyze:&lt;/p&gt;

&lt;p&gt;API modeling and documentation strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON Examples&lt;/li&gt;
&lt;li&gt;Data modeling with programming languages&lt;/li&gt;
&lt;li&gt;OpenAPI&lt;/li&gt;
&lt;li&gt;Less standard API description languages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;API development workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server first, client later&lt;/li&gt;
&lt;li&gt;Code-first&lt;/li&gt;
&lt;li&gt;Bi-directional testing&lt;/li&gt;
&lt;li&gt;Developing the API client against manually crafted mocks&lt;/li&gt;
&lt;li&gt;Developing the API client against the spec using mock servers&lt;/li&gt;
&lt;li&gt;Validating the server against the spec using contract testing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll begin by analyzing API modeling and documentation strategies and then I’ll analyze API development workflows. I’ll close the article with a summary and discussion.&lt;/p&gt;

&lt;h1&gt;
  
  
  API modeling and documentation
&lt;/h1&gt;

&lt;p&gt;API modeling refers to the process of designing the API endpoints and schemas, while API documentation refers to the process of describing the API design. In this section, I analyze the most common strategies that I’ve come across for modeling and documenting APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Examples
&lt;/h3&gt;

&lt;p&gt;With JSON Examples, we design the API endpoints and their associated payloads in the form of JSON examples. So instead of having schemas for the payloads, we &lt;strong&gt;have examples of what the payloads look like&lt;/strong&gt;. This approach works fine for very simple APIs. However, APIs with complex schemas that include optional properties or properties with multiple types aren’t easy to document with JSON Examples - it means having different JSON examples for the same payload. And when the payload design changes, it means updating all the JSON examples. There are three big flaws in this method:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incomplete documentation&lt;/strong&gt;: JSON Examples aren’t usually exhaustive and some cases are typically missing (specially for complex payloads); it’s also very easy to produce examples that contain mistakes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance burden&lt;/strong&gt;: as the API design evolves, we need to update all the JSON examples. In my experience, it’s common to forget to update some examples, which ends up making the collection of examples unreliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High noise-to-signal ratio&lt;/strong&gt;: With multiple examples per payload, it’s difficult to get an idea of how the API actually works. You end up having to infer the schemas by collating multiple examples.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Data modeling with programming languages
&lt;/h3&gt;

&lt;p&gt;This is a surprisingly common approach to API documentation. In this approach, developers enumerate the API endpoints and model their payloads as types using programming languages (such as TypeScript). The appeal of this approach is that the defined models are reusable straight away in API client and server code.&lt;br&gt;
When properly done, this approach is quite powerful, as it allows you to define reusable models, optional properties, and properties with multiple types. It also has a nice and clear syntax that everyone (who’s familiar with the chosen language) understands. The downside is that we’re mixing data types with data schemas. An API schema is not exactly a type - it’s far more precise than that. A schema allows you to do things that you can’t do with types, like defining the minimum length of a string or array, describing dependencies between properties, and so on.&lt;br&gt;
The better version of this strategy is when the models can be exported to JSON Schema or at least can be used to generate JSON Examples. Any update to the models can come with an update list of JSON Examples that API client developers can use for their testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAPI
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;gold standard&lt;/strong&gt;. The &lt;a href="https://www.openapis.org/"&gt;OpenAPI&lt;/a&gt; specification was created to satisfy the documentation needs of REST APIs, and for all of its imperfections, it really does a great job of describing how an API works.&lt;br&gt;
Unfortunately, this approach isn’t quite as common among self-claimed API-first developers as one would expect. Why is that? When I ask software developers why they don’t use OpenAPI to document their API designs, the most common answer is that they find OpenAPI &lt;strong&gt;difficult to understand&lt;/strong&gt; and to work with.&lt;br&gt;
Behind that reasoning, there seems to be an assumption here that you must write your OpenAPI specifications by hand. However, there really are many different ways to produce OpenAPI documentation: you can use a framework to generate documentation from code, or your can use one of the many tools that make it easier to produce OpenAPI documentation, like &lt;a href="https://learning.postman.com/docs/designing-and-developing-your-api/the-api-workflow/"&gt;Postman’s API Builder&lt;/a&gt;, &lt;a href="https://insomnia.rest/product/design"&gt;Insomnia’s Designer&lt;/a&gt;, or &lt;a href="https://stoplight.io/studio"&gt;Stoplight’s Studio&lt;/a&gt;.&lt;br&gt;
Using documentation-generation tools is very effective for those less familiar with OpenAPI. What I’d say is that you must check out the output of those tools to make sure it really aligns with the API design you want to achieve, and make any necessary amendments.&lt;br&gt;
OpenAPI has a large community and a vast ecosystem of tools and frameworks that make it easier to design, build, test, and work with APIs. Once you have your OpenAPI documentation at hand, you can leverage this ecosystem and turbocharge your API development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Less standard API description languages
&lt;/h3&gt;

&lt;p&gt;A common complaint about OpenAPI is that it’s difficult to learn and to read. Consequently, over the years we’ve seen many alternatives to OpenAPI, such as &lt;a href="https://en.wikipedia.org/wiki/RAML_(software)"&gt;RAML&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Web_Application_Description_Language"&gt;WADL&lt;/a&gt;, &lt;a href="https://apiblueprint.org/"&gt;API Blueprint&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/Overview_of_RESTful_API_Description_Languages#List_of_RESTful_API_DLs"&gt;others&lt;/a&gt;. The problem with many of these alternatives is that in most cases they &lt;strong&gt;aren’t really more readable&lt;/strong&gt; or easier to learn.&lt;br&gt;
Simpler description languages also tend to support less capabilities for documenting API features. Finally, being less standard, those alternatives have &lt;strong&gt;smaller communities, and smaller ecosystems&lt;/strong&gt; of tools and frameworks than OpenAPI, which makes for a poor development experience.&lt;/p&gt;

&lt;h1&gt;
  
  
  API development workflows
&lt;/h1&gt;

&lt;p&gt;API development workflow refers to the process followed to build an API. When it comes to API development workflows, the biggest differentiator is whether you have an API specification in place before you begin implementing, or whether the API specification comes after you implemented the API. As I mentioned earlier, this analysis is for API-first strategies, so in all cases I assume that there has been an element of API design, with the exception of the “Server first, client later” strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server first, client later
&lt;/h3&gt;

&lt;p&gt;In this approach, you build the API server first, and then you build the API client. In the early days of API development, this was a very common approach. Surprisingly, this approach still exists. The idea is that &lt;strong&gt;you don’t know what the API is going to look like or how it’s going to work until it’s built&lt;/strong&gt;, and therefore it doesn’t make sense to build the client before the server is implemented.&lt;br&gt;
This approach often goes hand in hand with a &lt;strong&gt;lack of API design and documentation practices&lt;/strong&gt;. I remember following this approach many years ago, and it was most frustrating. Documentation was an afterthought of the API development process, and you’d often have to hack around the endpoints or dive into the server code to figure out how the API worked.&lt;br&gt;
Sadly, I’ve also encountered this approach among teams that design and document their APIs first. The reason why client developers decide to wait for the server implementation even if the documentation is available is because they &lt;strong&gt;don’t trust that server developers will comply with the API specification&lt;/strong&gt;, which means that the API server isn’t validated against the specification. The obvious risk of this approach is ending up with an API server implementation that doesn’t reflect the intended API design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code-First
&lt;/h3&gt;

&lt;p&gt;Code-first generally fits within the concept of &lt;strong&gt;self-documenting APIs&lt;/strong&gt;. This approach is very popular among backend developers. The idea is to use API development frameworks that generate API documentation from our code, and to publish that documentation.&lt;br&gt;
Advocates of this approach claim that this is the &lt;strong&gt;only way to offer reliable API documentation and to keep it up to date&lt;/strong&gt;, since it automatically updates itself when you change the code. The problem with this approach is that it presumes that the &lt;strong&gt;API implementation must be the source of truth&lt;/strong&gt;. In practice, this approach means that the server isn’t checked for compliance against the API design.&lt;br&gt;
This approach makes the lives of API client developers miserable, since new server releases can easily break the existing API integration. As an API client developer, it’s very frustrating to spend hours testing your code against the server, only for a new server release to introduce changes in the API. A recent &lt;a href="https://www.reddit.com/r/Frontend/comments/s9gf1p/dealing_with_backend_developers/"&gt;post&lt;/a&gt; in Reddit illustrated well the resentment this approach creates among client developers. You can argue that better team communication can prevent this kind of problem. However, if you’ve been in software long enough, you know that communication isn't enough. You need to have &lt;strong&gt;automatic validation&lt;/strong&gt; in place.&lt;br&gt;
Another disadvantage of this approach is that API documentation generation tools are usually limited and sometimes can be wrong. For example, most frameworks don’t allow you to create OpenAPI links. Documenting security schemes is also challenging unless you use the framework’s specific security implementation, which may not satisfy your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bi-directional testing
&lt;/h3&gt;

&lt;p&gt;This approach is popular among the contract-testing community and it was popularized by &lt;a href="https://docs.pactflow.io/docs/workshops/bi-directional-contract-testing/"&gt;Pactflow&lt;/a&gt;. The idea is to implement the server with a self-documenting framework and to build the API client using manually crafted mocks for testing. Then you generate the API specification for the backend and the API specification for the client. Then you compare both specifications and see whether they agree. The nice thing about this approach is that &lt;strong&gt;it forces conciliation between the server and the client&lt;/strong&gt; before you release the code.&lt;br&gt;
The downside of this approach is that neither the server nor the client are directly implemented against the API design. Instead of using the API specification as the single source of truth, this approach lets the server and the client generate their own specifications, and it’s up to the developers to resolve the differences between them. This can cause unnecessary friction and delays in the API development process and can make discussions more difficult, involving specific implementation details of each side of the API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developing the API client against manually crafted mocks
&lt;/h3&gt;

&lt;p&gt;In this approach, you &lt;strong&gt;manually create mocks to build the API client&lt;/strong&gt;. The most traditional version of this approach involves creating mock responses in the form of JSON examples, while recent &lt;a href="https://github.com/typicode/json-server"&gt;frameworks&lt;/a&gt; allow you to run mock servers based on those JSON examples and some additional custom configuration.&lt;br&gt;
This is the most traditional approach to building API clients, but also the most common to this date, and sadly also the most error-prone. As we discussed earlier, JSON Examples tend to be incomplete and often wrong. They’re also a maintenance burden and become deprecated as soon as the API changes.&lt;br&gt;
The biggest limitation of this approach is that it &lt;strong&gt;doesn’t take advantage of a full API specification&lt;/strong&gt;, and as the API design changes, there’s a potentially growing drift between JSON examples and the actual API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developing the API client against the spec using mock servers
&lt;/h3&gt;

&lt;p&gt;In this approach, you first produce an API specification, and then you run &lt;strong&gt;mock servers directly against the specification&lt;/strong&gt;. Then you build the client against those mock servers. &lt;strong&gt;A mock server is a fake server that replicates the behavior of the real server&lt;/strong&gt; based on the API specification. This is the &lt;strong&gt;best approach for building API clients&lt;/strong&gt;, since it gives you the closest experience to interacting with and testing against the real API server. It’s a big advantage over traditional approaches like using manually crafted mock responses, since you don't need to  create and maintain JSON Examples anymore.&lt;br&gt;
In my experience, running API mock servers against the specification is very effective, although &lt;strong&gt;not all API designs are easily mockable&lt;/strong&gt;. For example, APIs that return time series data in which the dates must be sequential and within certain bounds are difficult to mock. APIs with badly designed data models can be very difficult to mock as well (see examples in this &lt;a href="https://www.microapis.io/blog/how-bad-models-ruin-an-api"&gt;article&lt;/a&gt;). In those cases, it’s still possible to leverage mock servers as long as you can provide examples or additional configuration.&lt;br&gt;
There’re many different ways to launch API &lt;a href="https://youtu.be/prGzBqUAXi4?t=1334"&gt;mock servers&lt;/a&gt; and this is a list of the most commonly used tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/stoplightio/prism"&gt;Stoplight’s prism&lt;/a&gt;&lt;/strong&gt;: the easiest way to launch a simple mock server locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://microcks.io/"&gt;Microcks&lt;/a&gt;&lt;/strong&gt;: an increasingly popular framework for running mock servers locally and in the cloud, with nice visualization dashboards and many other features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learning.postman.com/docs/designing-and-developing-your-api/mocking-data/setting-up-mock/"&gt;Postman&lt;/a&gt;&lt;/strong&gt;: a popular choice for those who’re already familiar with Postman, allowing integration with existing collections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.mocklab.io/docs/swagger/"&gt;Wiremock/MockLab&lt;/a&gt;&lt;/strong&gt;: a classic in the API mocking space, MockLab allows you to customize your mock server with your own stubs.&lt;/li&gt;
&lt;li&gt;(upcoming) &lt;a href="https://microapis.io/mock"&gt;microapis.io&lt;/a&gt;: in my previous work, I’ve always come across annoying limitations in the previously mentioned tools, like the inability to save results, add examples outside of the API documentation, leverage OpenAPI links, or obtain randomly successful and unsuccessful responses with varying payloads and response latencies. I'm currently building microapis.io’s mocker to address these limitations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Validating the server against the spec using contract testing tools
&lt;/h3&gt;

&lt;p&gt;In this approach, you produce an API specification first, then you &lt;strong&gt;build the API against the specification&lt;/strong&gt;, and then you &lt;strong&gt;validate your implementation against the specification&lt;/strong&gt; using automated API testing tools. This is the &lt;strong&gt;most reliable approach for building API servers&lt;/strong&gt;, since it’s the only one that holds the server accountable and validates the implementation against the source of truth.&lt;br&gt;
Unfortunately, this approach isn’t as common as it should be. One of the reasons why it isn’t so common is because it requires you to produce the API specification first, which, as we saw earlier, puts off many developers who don’t know how to work with OpenAPI. However, like I said before, generating OpenAPI specifications doesn’t need to be painful since you can use tools for that.&lt;br&gt;
In this approach, you use automated API testing tools to validate your implementation. Tools like &lt;a href="https://github.com/apiaryio/dredd"&gt;Dredd&lt;/a&gt; and &lt;a href="https://github.com/schemathesis/schemathesis"&gt;schemathesis&lt;/a&gt;. These tools work by parsing your API specification and automatically generating tests that ensure your implementation complies with the specification. They look at every aspect of your API implementation, including use of headers, status codes, compliance with schemas, and so on. The most advanced of these tools at the moment is schemathesis, which I highly encourage you to check out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary tables
&lt;/h1&gt;

&lt;p&gt;The below tables summarize the analysis from the previous sections and scores the maturity of each strategy and workflow on a scale of 0 to 3, with 0 being the most immature and 3 being the most mature.&lt;/p&gt;

&lt;h3&gt;
  
  
  API documentation methods
&lt;/h3&gt;

&lt;p&gt;Like I said before, OpenAPI is the gold standard for documenting REST APIs - it’s the most widely supported documentation format and it offers the highest degree of granularity for documenting API features, hence it gets the highest maturity score.&lt;br&gt;
JSON Examples are the most basic form of API documentation. They tend to be incomplete and inaccurate, and they’re difficult to maintain, hence they represent the most immature type of API modeling and documentation.&lt;br&gt;
Modeling with programming languages is better since it forces you to think about data models as opposed to specific examples, hence the score of 1.&lt;br&gt;
Finally, using a proper API description language, even if it isn’t widely supported, is still better than using JSON Examples or programming language models, hence the score of 2.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API doc method&lt;/th&gt;
&lt;th&gt;Benefits&lt;/th&gt;
&lt;th&gt;Challenges&lt;/th&gt;
&lt;th&gt;Documentation format&lt;/th&gt;
&lt;th&gt;Maturity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenAPI&lt;/td&gt;
&lt;td&gt;Highly detailed docs&lt;/td&gt;
&lt;td&gt;Learning OpenAPI&lt;/td&gt;
&lt;td&gt;JSON or YAML&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Less standard description languages&lt;/td&gt;
&lt;td&gt;Very customizable docs&lt;/td&gt;
&lt;td&gt;Smaller ecosystem&lt;/td&gt;
&lt;td&gt;Custom file formats&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Programming languages&lt;/td&gt;
&lt;td&gt;Reusable code&lt;/td&gt;
&lt;td&gt;Inability to represent schema constraints&lt;/td&gt;
&lt;td&gt;Language-specific files&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON Examples&lt;/td&gt;
&lt;td&gt;Real payload examples&lt;/td&gt;
&lt;td&gt;Maintenance burden&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  API development workflows
&lt;/h3&gt;

&lt;p&gt;Developing and validating against a unique source of truth is the most effective way for delivering reliable API integrations, hence why “API server tested against the API spec” and “API client tested against the API spec” get the highest rating.&lt;br&gt;
On the other side of the spectrum, the “Server first, client later” strategy is the most ineffective and unreliable way to deliver API integrations since it doesn’t validate the server implementation and it rarely exposes reliable documentation, hence the score of 0.&lt;br&gt;
Code-first and API client against manual mocks are sub-optimal since they don’t enforce a single source of truth, but at least they have a concept of API documentation, hence the score of 1.&lt;br&gt;
Bi-directional testing forces us to conciliate the API client and server implementations, which is a neat feature to ensure reliable integrations. However the lack of a single source of truth for reference and validation causes friction and earns it the score of 2.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API Workflow&lt;/th&gt;
&lt;th&gt;Benefits&lt;/th&gt;
&lt;th&gt;Challenges&lt;/th&gt;
&lt;th&gt;Maturity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API server tested against the API spec&lt;/td&gt;
&lt;td&gt;Ensures API server accurately reflects the spec&lt;/td&gt;
&lt;td&gt;Producing the OpenAPI documentation&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API client tested against the API spec&lt;/td&gt;
&lt;td&gt;Accurate reflection of how the API server works&lt;/td&gt;
&lt;td&gt;Some APIs are not easily mocked&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bi-directional testing&lt;/td&gt;
&lt;td&gt;Forces conciliation between client and server&lt;/td&gt;
&lt;td&gt;Doesn’t rely on a single source of truth&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API client against manual mocks&lt;/td&gt;
&lt;td&gt;Testing against payload examples&lt;/td&gt;
&lt;td&gt;Non-comprehensive and quickly deprecated examples&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code-first&lt;/td&gt;
&lt;td&gt;Accurately documented API implementation&lt;/td&gt;
&lt;td&gt;Unaccounted/untested API changes&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server first, client later&lt;/td&gt;
&lt;td&gt;You know how the API server works&lt;/td&gt;
&lt;td&gt;Undocumented API changes&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;APIs are fundamental components of modern platforms, and they give us access to many of services across the Internet. The sustainability of many businesses depends on having robust and reliable APIs. That’s why &lt;strong&gt;it’s important to invest in solid API modeling and documentation processes and development workflows&lt;/strong&gt;. Hopefully, the discussion in this article has given you some insight into the different ways you can approach these tasks, and how they fit within the whole spectrum of choices.&lt;br&gt;
Although I do have a preference towards the most mature API documentation and development workflows, I reckon &lt;strong&gt;it isn’t always easy or possible to get started straight away with them&lt;/strong&gt;. Embracing API design, using the OpenAPI specification, and leveraging contract-testing tools require skills and knowledge that sometimes aren’t there. They also require an additional investment of time and resources that sometimes cannot be afforded. And in fairness, there are situations, when your APIs are very simple and not business-critical, in which you may be able to get away with plain JSON examples and simple development workflows.&lt;br&gt;
The goal of the API-first development maturity framework is to make you aware of the &lt;strong&gt;benefits and constraints of each approach&lt;/strong&gt;, so that given your specific circumstances, you can &lt;strong&gt;assess the risk of each approach and choose the strategy that best fits your needs&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;If you enjoyed reading this article and found it useful, you’ll like my book &lt;strong&gt;&lt;a href="http://mng.bz/jy4x"&gt;Microservice APIs&lt;/a&gt;&lt;/strong&gt;. It teaches you everything you need to know to build APIs from the ground up, including design, documentation, implementation, testing, security, and deployments!&lt;/p&gt;

&lt;p&gt;You can download &lt;strong&gt;two chapters&lt;/strong&gt; of the book for &lt;strong&gt;free&lt;/strong&gt; from this &lt;a href="https://www.microapis.io/resources/microservice-apis-in-python"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also use the following code to obtain a &lt;strong&gt;40% discount&lt;/strong&gt; when buying the book: &lt;strong&gt;slperalta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I also run a very popular series of &lt;strong&gt;workshops&lt;/strong&gt;, both free and paid ones, in which I teach best practices and advanced patterns for building microservices and APIs. I’d love to see you around in one of my workshops! An updated list of upcoming workshops is always available here: &lt;a href="https://microapis.io/workshops"&gt;https://microapis.io/workshops&lt;/a&gt; and here &lt;a href="https://www.eventbrite.co.uk/o/jose-haro-peralta-43771267993"&gt;https://www.eventbrite.co.uk/o/jose-haro-peralta-43771267993&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>microservices</category>
      <category>rest</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Documentation-driven development for APIs: what is it, how do you do it and why you should do it?</title>
      <dc:creator>José Haro Peralta</dc:creator>
      <pubDate>Sun, 27 Feb 2022 23:18:23 +0000</pubDate>
      <link>https://dev.to/abunuwas/documentation-driven-development-for-apis-what-is-it-how-do-you-do-it-and-why-you-should-do-it-1ioh</link>
      <guid>https://dev.to/abunuwas/documentation-driven-development-for-apis-what-is-it-how-do-you-do-it-and-why-you-should-do-it-1ioh</guid>
      <description>&lt;p&gt;The code for this post is available under: &lt;a href="https://github.com/abunuwas/documentation-driven-development"&gt;https://github.com/abunuwas/documentation-driven-development&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation-driven development is an approach to API development where you write the documentation first, and then implement the API as per the specification. If you have any clients of the API within your system (for example a frontend application) then you implement them against the specification as well. This approach is often also called API-first.&lt;/p&gt;

&lt;p&gt;There’s often this idea that API changes should be driven by the backend, and that the backend can change the API at any time and then the API client (for example a frontend application) has to comply with whichever arbitrary changes were made to the backend.&lt;/p&gt;

&lt;p&gt;Many developers have the idea that you can’t start working on an API client (for example a frontend application) until the backend API is implemented. This is simply not true: &lt;strong&gt;if you write the documentation first, then both the client application and the API server can be implemented at the same time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, not everything counts as documentation when it comes to APIs. APIs must be documented in standard formats, such as OpenAPI, so that we can leverage the benefits of documentation-driven development. I’ve seen many teams documenting their API using tools such as Confluence, Google Docs, Sharepoint, Dropbox Papers, and similar. This doesn’t work because we can’t generate standard specifications from them that we can use in combination with other tools to test our API implementation.&lt;/p&gt;

&lt;p&gt;How does this work in practice? And is it beneficial at all? I’m going to show you how to practice documentation-driven development to develop a REST API with Flask. The approach is the same for any other kind of API and it works with any other framework.&lt;/p&gt;

&lt;p&gt;We’ll implement a simple API to manage a list of to-do items. The schema for the to-do item will have the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ID in UUID format&lt;/li&gt;
&lt;li&gt;Created timestamp&lt;/li&gt;
&lt;li&gt;Task as a string&lt;/li&gt;
&lt;li&gt;Status as a string, which can be one of the following: pending, progress, completed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The attributes ID created will be set by the backend and therefore will be read-only for the API client. The attribute task represents the task that has to be done, and the status attribute represents the status of the task and can only take one of the enumerated values. For best practice and reliability, we’ll invalidate any requests that include any properties not listed in the schema.&lt;/p&gt;

&lt;p&gt;We’ll have two URL paths to manage our list of to-do items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;/todo&lt;/li&gt;
&lt;li&gt;/todo/{item_id}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;/todo represents the collection of to-do items, and we’ll use it to fetch a list of our to-do items and to create new to-do items. We’ll use /todo/{item_id} to manage specific tasks, and we’ll use it to retrieve the details of a specific task, to update it, and to delete it.&lt;/p&gt;

&lt;p&gt;Now that we have gone through the process of designing our API, let’s document it first before we jump onto the implementation!&lt;/p&gt;

&lt;p&gt;I mentioned at the beginning that we’d be implementing a REST API, so we’ll use OpenAPI to document it. Create a file called oas.yaml and write the following content to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.3&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TODO API&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API that allows you to manage a to-do list&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/todo/&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Returns a list of to-do items&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A JSON array of tasks&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/GetTaskSchema'&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Creates an task&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/CreateTaskSchema'&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;201'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A JSON representation of the created task&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/GetTaskSchema'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="s"&gt;/todo/{item_id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;item_id&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uuid&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Returns the details of a task&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A JSON representation of a task&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/GetTaskSchema'&lt;/span&gt;
      &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;404'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/NotFound'&lt;/span&gt;
    &lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Updates an existing task&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/CreateTaskSchema'&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A JSON representation of a task&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/GetTaskSchema'&lt;/span&gt;
      &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;404'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/NotFound'&lt;/span&gt;
    &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deletes an existing task&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;204'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The resource was deleted successfully&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;404'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/responses/NotFound'&lt;/span&gt;
&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;NotFound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The specified resource was not found.&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Error'&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;task&lt;/span&gt;
      &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;progress&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;completed&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
        &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;created&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;priority&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;status&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;task&lt;/span&gt;
      &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uuid&lt;/span&gt;
        &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Date in the form of UNIX timestmap&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;progress&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;completed&lt;/span&gt;
        &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the API specification we are in an excellent position to start implementing the API server. If you’re working with another team that has to implement a client application for the API, such as a frontend application, make sure you make the API specification available to all teams in a central location, such as a GitHub repository or URL endpoint. I’ll write another post later on illustrating how you can do that.&lt;/p&gt;

&lt;p&gt;To implement the API we’ll use Flask in combination with flask-smorest. Flask-smorest is a REST API framework that uses marshmallow to validate schemas. For illustration purposes we’re going to keep this super simple and the whole app will be in one file, and the list of items will be represented by an in-memory list. In a real app you want to use persistent storage and make sure different components go into different modules. The only dependency you’ll need to get the code working is flask-smorest, so make sure it’s installed. Create a file called app.py and copy the following content to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MethodView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask_smorest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Blueprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abort&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;marshmallow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EXCLUDE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'API_TITLE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'TODO API'&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'API_VERSION'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'1.0.0'&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'OPENAPI_VERSION'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'3.0.3'&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'OPENAPI_JSON_PATH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"api-spec.json"&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'OPENAPI_URL_PREFIX'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'OPENAPI_SWAGGER_UI_PATH'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/docs"&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'OPENAPI_SWAGGER_UI_URL'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/swagger-ui-dist/"&lt;/span&gt;

&lt;span class="n"&gt;API_TITLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Orders API'&lt;/span&gt;
&lt;span class="n"&gt;API_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'1.0.0'&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDE&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneOf&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'progress'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'completed'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;blueprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'/todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'API that allows you to manage a to-do list'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;TODO_LIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TODO_LIST&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;TODO_LIST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/&amp;lt;item_id&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TODO_LIST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Item not found.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TODO_LIST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Item not found.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TODO_LIST&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;TODO_LIST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Item not found.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blueprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run the app with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ FLASK_APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;app:app flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As per the app configuration, you can visit the API documentation auto-generated from the app with a Swagger UI theme under /docs.&lt;/p&gt;

&lt;p&gt;Great, now, how do we test this implementation to make sure it’s compliant with the specification? You can use a bunch of different tools and frameworks to accomplish this. Here I’ll show you how you can do it with Dredd. Dredd is an npm package, so to install it run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;dredd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run dredd, execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./node_modules/.bin/dredd oas.yaml http://127.0.0.1:5000 — server “flask run”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this now, you’ll get an error saying that we need to provide an example for the item_id parameter in the /todo/{item_id} URL path. You’ll see also some warnings about issues with the specification format, which you can safely ignore as the API spec is valid (you can validate that with external tools like this: &lt;a href="https://editor.swagger.io/"&gt;https://editor.swagger.io/&lt;/a&gt;). Let’s go ahead and add an example fo item_id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;/todo/{item_id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;item_id&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uuid&lt;/span&gt;
        &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;d222e7a3-6afb-463a-9709-38eb70cc670d&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run Dredd now again, you’ll get 5 tests passing and 3 failures. If you look closer at the failures, you’ll see that all of them are related to operations on existing resources under the /todo/{item_id} URL path. It seems Dredd is picking up the example ID we provided in the spec and expecting a resource with such ID to exist. Obviously no resource exists until we start running the API, so we want Dredd to actually create a resource first using POST /todo/, fetch the ID of the created resource, and use it to test the endpoints under the /todo/{item_id} URL path. How do we do that? Using dredd hooks!&lt;/p&gt;

&lt;p&gt;Dredd hooks offer a simple interface that allow use to take action before and after a transaction. Every transaction is identified by a “name”, which is a combination of different parameters that uniquely identify an operation. To list all names available in your specification, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./node_modules/.bin/dredd oas.yaml http://127.0.0.1:5000 — server “flask run” &lt;span class="nt"&gt;--&lt;/span&gt; names
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The available names are each of the info blocks listed by the command:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k6jO2nQ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uif454bkv43usb27lr8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6jO2nQ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uif454bkv43usb27lr8i.png" alt="Image description" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want the names of the successful actions under the /todo/{item_id} URL path, namely actions that return a success status code such as 200 and 204 (i.e. not those returning 404). Create a file called hooks.y and copy the following content to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dredd_hooks&lt;/span&gt;


&lt;span class="n"&gt;response_stash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dredd_hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/todo/ &amp;gt; Creates an task &amp;gt; 201 &amp;gt; application/json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_created_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'results'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'fields'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'values'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'actual'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;task_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_payload&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;response_stash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created_task_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dredd_hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/todo/{item_id} &amp;gt; Returns the details of a task &amp;gt; 200 &amp;gt; application/json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_get_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'fullPath'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'/todo/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response_stash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created_task_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dredd_hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/todo/{item_id} &amp;gt; Updates an existing task &amp;gt; 200 &amp;gt; application/json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_put_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'fullPath'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'/todo/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response_stash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created_task_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dredd_hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/todo/{item_id} &amp;gt; Deletes an existing task &amp;gt; 204'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_delete_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'fullPath'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'/todo/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response_stash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created_task_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ll talk more about dredd and dredd hooks in another tutorial. You can run Dredd with these hooks with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./node_modules/.bin/dredd oas.yaml http://127.0.0.1:5000 — &lt;span class="nv"&gt;hookfiles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hooks.py — &lt;span class="nv"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python — server “flask run”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now everything passes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zSNChKrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/voy1hkiv8812lbt04m8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zSNChKrh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/voy1hkiv8812lbt04m8z.png" alt="Image description" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right, so now we are certain that our API implementation complies with the specification. If another team working on a frontend application has made similar tests, we can be quite certain that both the server and the frontend app will integrate nicely and without errors. Or at least we won’t encounter integration errors due to non-compliance with the API.&lt;/p&gt;

&lt;p&gt;Let’s now say that we want to make a change to the API. As it turns, we’d like to be able to assign a priority to each task. This will result in a new field in task resource payload called “priority”, which may have one of the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;low&lt;/li&gt;
&lt;li&gt;medium&lt;/li&gt;
&lt;li&gt;high&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default value will be “low”.&lt;/p&gt;

&lt;p&gt;How should we approach this change? We are practicing documentation-driven development, so first we’ll change the specification. Once we’ve updated the specification, we can change both the backend and the frontend. &lt;strong&gt;Any change to the backend API that doesn’t comply with the spec will result in a failed test and shouldn’t be released&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The updated schemas in oas.yaml look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;task&lt;/span&gt;
      &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;medium&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;progress&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;completed&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
        &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;created&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;priority&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;status&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;task&lt;/span&gt;
      &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uuid&lt;/span&gt;
        &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Date in the form of UNIX timestmap&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;medium&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
          &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;low&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pending&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;progress&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;completed&lt;/span&gt;
        &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we now run the Dredd command the test will fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./node_modules/.bin/dredd oas.yaml http://127.0.0.1:5000 — &lt;span class="nv"&gt;hookfiles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hooks.py — &lt;span class="nv"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python — server “flask run”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wMlEQZu6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ojfzh93r5kdxjqf950bz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wMlEQZu6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ojfzh93r5kdxjqf950bz.png" alt="Image description" width="800" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix this we only need to update our marshmallow schemas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDE&lt;/span&gt;
    &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'low'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneOf&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;'low'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'medium'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'high'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneOf&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'progress'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'completed'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetTaskSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateTaskSchema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run the Dredd command again, the tests now pass. In this example, updating the marshmallow schemas is sufficient to update the app. In a real application, you’ll be persisting data to a database and you’ll need to run some migrations to update your models as well.&lt;/p&gt;




&lt;p&gt;If you want to learn more about API integrations, have a look at my new book Microservice APIs in Python”. It’s just been released through the Manning Early Access Program (MEAP), which means it’s still under development and you get a chance to give your feedback and request new content before the book is in print. Access to the book: &lt;a href="http://mng.bz/nz48"&gt;http://mng.bz/nz48&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in the book, you can use the code &lt;strong&gt;slperalta&lt;/strong&gt; to get a 40% discount. You can also download two chapters for free from the following URL: &lt;a href="https://www.microapis.io/resources/microservice-apis-in-python"&gt;https://www.microapis.io/resources/microservice-apis-in-python&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>microservices</category>
      <category>testing</category>
      <category>software</category>
    </item>
    <item>
      <title>How employers can navigate the current software developer market</title>
      <dc:creator>José Haro Peralta</dc:creator>
      <pubDate>Tue, 22 Feb 2022 10:17:34 +0000</pubDate>
      <link>https://dev.to/abunuwas/how-employers-can-navigate-the-current-software-developer-market-4p5p</link>
      <guid>https://dev.to/abunuwas/how-employers-can-navigate-the-current-software-developer-market-4p5p</guid>
      <description>&lt;p&gt;The big skills crunch is coming and we need to rethink how we approach talent acquisition. According to a &lt;a href="https://www.mckinsey.com/business-functions/organization/our-insights/beyond-hiring-how-companies-are-reskilling-to-address-talent-gaps"&gt;Mckinsey report&lt;/a&gt; from last year, most businesses expect to suffer from skills gaps soon and are looking to alleviate the problem by upskilling their workforce.&lt;/p&gt;

&lt;p&gt;Demand for software developers is at nearly an all-time high, if not an all-time high. Developer salaries are reaching record levels. From the job boards, it appears that companies are struggling to hire senior developers more than anything else.&lt;/p&gt;

&lt;p&gt;The ratio of senior developers to junior developers has always been small. According to &lt;a href="http://blog.cleancoder.com/uncle-bob/2014/06/20/MyLawn.html"&gt;Bob Martin's famous analysis&lt;/a&gt;, the population of software developers roughly doubles every five years. These days, however, the ratio of senior to junior developers is extraordinarily small. In the most recent &lt;a href="https://insights.stackoverflow.com/survey/2020#developer-profile-years-coding-professionally"&gt;StackOverflow developer survey&lt;/a&gt;, developers with less than 5 years of experience make up almost half of the demographics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1aLt3z3A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7b1wn0g1g2qpnyvgcngh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1aLt3z3A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7b1wn0g1g2qpnyvgcngh.png" alt="Image description" width="770" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, businesses have strived to keep a balance between the amount of junior and senior developers on board. The idea was to have enough senior developers to allow them to coach and guide junior developers while still being able to focus on their own work. This balance is breaking.&lt;br&gt;
Amid fierce competition from the big players, many small and medium companies are giving up on their pursuit of senior developers and opting to bring in junior developers instead. What does this mean for small and medium companies?&lt;br&gt;
Having teams composed mainly of junior developers is not necessarily a problem. But it's a situation you need to manage. If you have very few senior developers, you can't rely on them to be able to coach and guide your junior developers while focusing on their own work. You have to change your approach to upskilling junior developers.&lt;br&gt;
Most companies don't have proper training programs for junior developers. They expect junior developers to somehow be able to grow and learn by themselves with the help and guidance of their more senior colleagues. In my experience, this works in certain situations, but it also leaves many otherwise capable developers behind. A recent &lt;a href="https://news.ycombinator.com/item?id=27596687"&gt;thread&lt;/a&gt; in Hackernews captured the feelings of most developers around this problem, and &lt;a href="https://news.ycombinator.com/item?id=27597232"&gt;one of the comments&lt;/a&gt; summarised how many of us see the situation:&lt;br&gt;
Devs don't spend money on expensive training courses and certs to gain skills; we just get hired somewhere and then pester our co-workers.&lt;/p&gt;

&lt;p&gt;In the current market conditions, you can't afford to leave any developer behind. You need to invest in your developers and make sure they receive the necessary support to grow into skilled developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to make training effective
&lt;/h2&gt;

&lt;p&gt;Now here's the important note for business managers: training your employees doesn't mean go tell them to do a course or two in Udemy or Coursera. Most online courses are good for basic training, but the real value comes from advanced training tailored to your needs and delivered in real time by subject-matter experts. You need to bring structured training as part of the job routine, and you need industry experts to deliver the training.&lt;/p&gt;

&lt;p&gt;Let's dissect the recommendation in the previous paragraph. In particular, let's look at the following questions: why real-time training? Why training tailored to your needs? And why training delivered by subject-matter experts?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The benefits of real time training&lt;/strong&gt;: real time training allows the instructor to interact with the audience, which has the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The instructor can adapt the content on the fly. For example, if some of the attendants never heard of certain concepts, the instructor can explain them in advance to make sure everybody is on the same page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The instructor can look at the audience's reactions and gauge whether they are following or not. Again, this allows the instructor to be adaptive to make sure the audience understands all the concepts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The audience can ask questions during the lecture if they don't understand something.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Training tailored to your needs&lt;/strong&gt;: it's great to do a course about AWS, or Kubernetes, or GraphQL, or any other topic. But if the course covers those topics exclusively from a generic point of view, there's a great chance that the content delivered doesn't address the needs of your team. If you're planning to use GraphQL, talk to the instructor about your use cases and the instructor will be able to adapt the course to your needs. That way the training will be a lot more effective for your employees, as they'll be able to relate their experience to it, and they'll be able to apply the lessons learned straight away into their job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Training delivered by experts&lt;/strong&gt;: you can argue that anyone can deliver training. And sure enough, everyone has something to teach. But to make the most of your corporate training, you want to engage subject matter experts. Not generalists or random professionals. You want instructors with hands-on experience in the field they're teaching, and with proven abilities to teach either through published books or courses. Think of the instructor as a consultant who'll not only teach your employees best practices around each topic, but also can advise on whether you're making the correct use of certain technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  But, why should I invest in training my employees?
&lt;/h2&gt;

&lt;p&gt;Many companies think investing in employee training is a waste of money. After all, corporate training workshops are expensive, and your employees will be "out of work" for a few days. But the reality is far from that. From experience, I can say that the right training delivered by the right person will save you tens of thousands of dollars. Yes, there's a hidden cost to lack of training.&lt;/p&gt;

&lt;p&gt;How's that possible? Consider the case of APIs, which is one of my subjects of expertise. If you ask around, most developers will say they have a solid understanding of how APIs and API authentication work. And sure enough, most developers have a fairly good idea of how APIs work. After all, we work with APIs all the time!&lt;/p&gt;

&lt;p&gt;But consuming an API is a very different task from producing one. Think of it this way: we can all read books, but that doesn't mean we're all writers. It takes a special skill and knowledge to produce something. When it comes to APIs, common knowledge gaps are around available API technologies with their pros and cons, documentation standards, how to work with an OpenAPI specification, JSON Schema syntax, how to leverage API documentation for testing and validation, or to run mock servers, best practices around the design of the endpoints and resource modelling, API security and authentication, and many others.&lt;/p&gt;

&lt;p&gt;Without guidance, each of the knowledge gaps listed in the previous paragraph typically cause development teams to lose weeks or even months doing research and learning. When deadlines are around the corner, it can be frustrating for everybody.&lt;/p&gt;

&lt;p&gt;And it's also expensive! &lt;a href="https://insights.stackoverflow.com/survey/2020#work-salary-by-developer-type"&gt;According to StackOverflow's 2020 developer survey&lt;/a&gt;, the median salary of a backend developer in the US is $120k. Assuming *&lt;strong&gt;&lt;em&gt;roughly&lt;/em&gt;&lt;/strong&gt;* 220 working days a year (11 months x 20 days), knowledge gaps cost businesses approximately $2,700 per developer per week. And assuming a developer only spends one month a year (it's usually more) filling knowledge gaps, the yearly cost sits at approximately $11k. In a team of ten developers, the cost rises to $110k per year! Bear in mind this is a conservative estimate.&lt;br&gt;
These spiralling costs can be avoided by incorporating training into the daily job. Training brings expert knowledge and guidance for your team. And it also makes happy teams. Software is a knowledge-based industry, and one of the reasons developers leave their job is because they're not learning new things anymore. To build a strong, confident, happy, and productive team, you need to embrace training.&lt;/p&gt;




&lt;p&gt;If you liked this article and would like to learn more about my work, please check out my websites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Personal website: &lt;a href="https://www.joseharoperalta.com"&gt;https://www.joseharoperalta.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;microapis.io: &lt;a href="https://microapis.io"&gt;https://microapis.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you'd like to learn more about microservice APIs, feel free to check out my book &lt;a href="http://mng.bz/nz48"&gt;Microservice APIs&lt;/a&gt;. You can use the following code to get a 40% discount: &lt;strong&gt;slperalta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can also download two chapters for free using the following link: &lt;a href="https://www.microapis.io/resources/microservice-apis-in-python"&gt;https://www.microapis.io/resources/microservice-apis-in-python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're currently working or looking to work with APIs and would like to arrange a training or consulting session, please feel free to reach out to me at &lt;a href="mailto:info@algorizm.co"&gt;info@algorizm.co&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>software</category>
      <category>jobs</category>
      <category>itmarket</category>
      <category>career</category>
    </item>
    <item>
      <title>How bad models ruin an API (or why design-first is the way to go)</title>
      <dc:creator>José Haro Peralta</dc:creator>
      <pubDate>Sun, 09 Jan 2022 17:22:42 +0000</pubDate>
      <link>https://dev.to/abunuwas/how-bad-models-ruin-an-api-or-why-design-first-is-the-way-to-go-5b4f</link>
      <guid>https://dev.to/abunuwas/how-bad-models-ruin-an-api-or-why-design-first-is-the-way-to-go-5b4f</guid>
      <description>&lt;h2&gt;
  
  
  Documenting API schemas is important
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.openapis.org/"&gt;OpenAPI&lt;/a&gt; allows you to define the schema of the data that’s exchanged over the API. You can define schemas for your query parameters, your request payloads, your response payloads, and more. Schemas are defined using a subset of &lt;a href="https://json-schema.org/"&gt;JSON Schema&lt;/a&gt; syntax, and typically a schema is an object with a collection of properties. The following is an example of a simple schema with OpenAPI valid syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt; 
    &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The point of having schemas in an API specification is to give clients an expectation of what the data that they’ll receive from the server will look like&lt;/strong&gt;. In that sense, schemas are super important, since they allow client developers to build their integrations correctly.&lt;/p&gt;

&lt;p&gt;Despite their usefulness, I often come across APIs that don’t provide schemas for their response payloads. These are typically APIs for internal consumption, usually an internal product of the company. In such cases, it seems developers think it’s an unnecessary overhead to write good documentation for the API. After all, it’s them who’re building the API server and the client at the same time. They talk to each other and they agree on what the API payloads look like. So what’s the point of documentation?&lt;/p&gt;

&lt;p&gt;The point of documentation is that at some point the composition of the team is going to change. At some point, new team members will join, and sometime in the future the original team members probably won’t be around any longer. In that case, having documentation is extremely useful if not indispensable. Without documentation, the only way to get an idea of what the API response payloads look like is by interacting with the server and harvesting responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  But not all schemas are the same
&lt;/h2&gt;

&lt;p&gt;In some cases, we do have response schemas documented in the API, but with very poor models. I recently came across a response payload model with a schema similar to below definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
          &lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;oneOf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When looking at the actual response payload, the data served by the API looked like this (not the actual data):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"engine_type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"2021–09–01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"electric"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"2021–09–01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"combustion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"Ford"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I call this a &lt;strong&gt;schemaless schema&lt;/strong&gt;, or &lt;strong&gt;free-form schema&lt;/strong&gt;. Schemas like this are useless. It’s not obvious how data is structured here. As it turned out, the data served by the API was meant to be represented in a table. The columns field tells us the column names of the table. The data field contains the values that must be populated for each column. In strict order.&lt;/p&gt;

&lt;p&gt;See what the problem is here? Various problems actually. To begin with, we’re relying on positional order to determine how a value maps to a property. That’s a lot of information that the client application has to have and to manage.&lt;br&gt;
It’s also problematic for the backend. The slightest change in the code could cause the values to come in the wrong order, or we might have arrays with missing elements, and everything would break or give us an erroneous representation of the data.&lt;/p&gt;

&lt;p&gt;Schemaless schemas make testing difficult. Tools like &lt;a href="https://github.com/apiaryio/dredd"&gt;Dredd&lt;/a&gt; and &lt;a href="https://github.com/schemathesis/schemathesis"&gt;Schemathesis&lt;/a&gt; rely on your API documentation to generate tests and validate your API responses. A collection of free-form arrays like the above model will pass nearly every test, even if the length of the arrays or their contents are wrong. Schemaless schemas are also useless for API mocking, which is a fundamental part of building reliable API integrations.&lt;/p&gt;

&lt;p&gt;The above schema can be improved with a few simple changes. Instead of relying on positional order in an array, we define an array of objects with explicit properties and values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;date&lt;/span&gt;
      &lt;span class="na"&gt;engine_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data from this schema might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"engine_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electric"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"engine_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"combustion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ford"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"02"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So much more understandable!! Isn’t it?? Designing good schemas is actually not that difficult. You just need to spend some time working on them. If that’s all it takes, how come we end up so often with bad schemas?&lt;/p&gt;

&lt;h2&gt;
  
  
  Code-first is a risky strategy
&lt;/h2&gt;

&lt;p&gt;This situation is actually pretty common in APIs that have been built with a code-first approach, without much design consideration. That was indeed the case of the above-mentioned API. I could bring in a lot more examples of bad API models resulting from lack of a design stage (i.e. resulting from code-first). On one occasion, I had to work with an API in which date fields looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apparently, the server and the client development teams couldn’t agree on what the date format should look like, so they opted for an object representation of the date. The funny thing about this model is it doesn’t even have a timezone field! In any case, date fields shouldn’t be a matter of debate. We have ISO standards. All languages and frameworks can handle ISO dates. OpenAPI accepts &lt;code&gt;date&lt;/code&gt; and &lt;code&gt;date-time&lt;/code&gt; as field types. If only developers had spent one minute looking at the OpenAPI documentation...&lt;/p&gt;

&lt;p&gt;On multiple occasions, I’ve had properties which are defined as an array of just one element. Something along the lines of the below example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;minItem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;maxItem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mean, why?? Why would you even do that? The property is singular (indicating it’s just one element). Arrays are for variable lengths of elements. Where in the world would you use arrays for just one element? The reason, again, was that the API had been built with a code-first approach. The part of the application that consumed this bit of data required input in the form of an array, and somewhere in the application the code would blow up if you gave it a list of more than one element.&lt;/p&gt;

&lt;p&gt;What is missing in this case is a separation of concerns between the business layer and the API layer, and a data transfer object (DTO) that translates the input from the API into the format that is accepted by the code internals. In fact, by skipping all these things, we’ve achieved an excellent degree of tight-coupling between low-level application code and the API. The consequences of this tight-coupling are disastrous, since the slightest change in the server code can break the API and/or the client application in unexpected ways. The best part? All of this could’ve been avoided by spending five minutes designing the interface before jumping straight into the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are we doomed?
&lt;/h2&gt;

&lt;p&gt;Are we doomed? No, we’re not doomed. The moral of the story is that to build a good API, you need to spend some time designing it before jumping into the implementation. That doesn’t mean you can’t code from the beginning. If you don’t like to write raw yaml or JSON, you can use an API documentation-generation framework to drive the design of your API from your code. Just make sure you don’t commit your code and release it as the actual API implementation before the design has been agreed upon.&lt;/p&gt;

&lt;p&gt;My recommendation is, once the design is agreed, use your documentation-generation framework to render the OpenAPI specification, make any changes to it if required, and commit the file. &lt;strong&gt;Your OpenAPI specification file becomes the source of truth for your API&lt;/strong&gt;. From that moment on, drive changes to your API from that file, and test your API implementation against that file. &lt;strong&gt;Don’t use the dynamically generated documentation as the source of truth&lt;/strong&gt;. Dynamically generated documentation is just a reflection of the backend code, which is not necessarily correct.&lt;/p&gt;




&lt;p&gt;If you liked this article, you’ll also like my book &lt;strong&gt;&lt;a href="http://mng.bz/jy4x"&gt;Microservice APIs&lt;/a&gt;&lt;/strong&gt;. The book is conceived as a one-stop guide for learning how to design and build microservices and how to drive their integration with APIs.&lt;/p&gt;

&lt;p&gt;You can download two chapters of the book for free from this &lt;a href="https://www.microapis.io/resources/microservice-apis-in-python"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you’re interested, you can get a 40% discount from the book’s price by using the following code on Manning’s website: &lt;strong&gt;slperalta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In addition to my blog, I also upload video tutorials to &lt;a href="https://www.youtube.com/channel/UCtrp0AWmJJXb50zb12XxTlQ"&gt;YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jose-haro-peralta/"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>api</category>
      <category>microservices</category>
      <category>rest</category>
      <category>openapi</category>
    </item>
  </channel>
</rss>
