API-First
development
From specification to code and back
Photo by Edho Pratama on Unsplash
Your host today
• Vasco Veloso
• Working in software development since 1996.
• Currently interested in designing software systems
and giving back to the community.
• Author and speaker.
• Portuguese living in the Netherlands.
linkedin.com/in/vascoveloso @vveloso
API-first design
The interface specification comes first.
• Dependencies are discovered.
• Data flows become apparent.
• Data needs can be challenged.
• Great input for threat modelling sessions.
3 Photo by Med Badr Chemmaoui on Unsplash
The problem…
There are many different ways to capture interface designs...
… that are not technical in nature …
… and all require further translation before implementation!
4
The problem…
Many API specifications have little value
• The interface is not complete.
• Examples are missing.
• Data formats or constraints are not defined.
• The specification is outdated.
• Code has changed but the specification did
not.
• There is little connection with the implementation.
5 Photo by Brett Jordan on Unsplash
API-First
Development
Photo by Andrew George on Unsplash
7
API-first development
The interface specification comes first.
Share
Control
Review
Stakeholders
CI/CD
8
API-first development
Capability enabler
cf. 2019 Accelerate State of DevOps Report (DORA)
✅
✅
9
API-first development
Teams with quality documentation are…
cf. 2020 Accelerate State of DevOps Report (DORA)
API-first development
The interface specification needs to be complete.
• All inputs are present.
• Data is properly described.
• Descriptions and examples are included.
Code needs to be generated from the
specification.
• Generate the server interface.
• Generate client code.
10
Photo
by
Julia
Joppien
on
Unsplash
API-first development
Advantages:
• The API specification is the single source of
truth.
• Code and specification are always in sync.
• Specification is good enough to generate code?
• Then it’s good enough to be used for
testing!
• Automatic implementation validation becomes
possible.
11 Photo by Glenn Carstens-Peters on Unsplash
 OpenAPI plugin
 Restler
 Schemathesis
12
API-first development
High-level workflow for developing the server:
Design API
Write OpenAPI
specification
Generate code
Implement & test
business rules
Deploy
Workflow
Refinement
Validate
implementation
Imagine a Java
service using
Spring…
Photo by Scott Graham on Unsplash
… with two operations …
paths:
'/hello':
get:
summary: 'Retrieve a greeting.'
operationId: 'getHelloWorld'
tags:
- "helloWorld"
responses:
'200':
description: 'OK: Returning a greeting.'
schema:
$ref: '#/definitions/Greeting'
'500':
description: 'Internal Server Error'
schema:
$ref: '#/definitions/ResponseError’
put:
summary: 'Set the greeting.'
operationId: 'putHelloWorld'
parameters:
- in: body
name: greeting
schema:
$ref: '#/definitions/Greeting'
required: true
description: 'The new greeting definition.'
tags:
- "helloWorld"
responses:
'200':
description: 'OK: The greeting was stored.'
schema:
$ref: '#/definitions/Greeting'
'500':
description: 'Internal Server Error'
schema:
$ref: '#/definitions/ResponseError'
… and two object definitions …
definitions:
ResponseError:
type: object
properties:
code:
type: string
example: 'E-1234'
description: 'Internal error code.'
required:
- code
Greeting:
type: object
properties:
text:
type: string
pattern: '[0-9a-z]'
maxLength: 100
example: 'Hello'
description: 'The greeting text.'
required:
- text
… using Maven with the OpenAPI plugin …
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.1.0</version>
<executions>
<execution>
<id>api-generation</id>
<goals><goal>generate</goal></goals>
<configuration>
<inputSpec>
${project.basedir}/src/main/openapi/spec.yaml
</inputSpec>
<output>${project.basedir}/gensrc</output>
<generatorName>spring</generatorName>
<modelPackage>api.example.model</modelPackage>
<apiPackage>api.example.resource</apiPackage>
<configOptions>
<interfaceOnly>true</interfaceOnly>
<dateLibrary>java8</dateLibrary>
<useTags>true</useTags>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
… generated an interface definition …
/**
* PUT /hello : Set the greeting.
*
* @param greeting The new greeting definition. (required)
* @return OK: The greeting was stored. (status code 200)
* or Internal Server Error: something has gone wrong. (status code 500)
*/
@ApiOperation(value = "Set the greeting.", nickname = "putHelloWorld", notes = "", response =
Greeting.class, tags={ "helloWorld", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK: The greeting was stored.", response = Greeting.class),
@ApiResponse(code = 500, message = "Internal Server Error: something has gone wrong.", response =
ResponseError.class) })
@PutMapping(
value = "/hello",
produces = { "application/json" },
consumes = { "application/json" }
)
default ResponseEntity<Greeting> putHelloWorld(@ApiParam(value = "The new greeting definition."
,required=true ) @Valid @RequestBody Greeting greeting) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
… with objects that can be validated.
public class Greeting {
@JsonProperty("text")
private String text;
public Greeting text(String text) {
this.text = text;
return this;
}
/**
* The greeting text.
* @return text
*/
@ApiModelProperty(example = "Hello", required = true, value = "The greeting text.")
@NotNull
@Pattern(regexp="[0-9a-z]") @Size(max=100)
public String getText() { return text; }
public void setText(String text) { this.text = text; }
}
Flipping it
around
Photo by Persnickety Prints on Unsplash
Flipping it around
• Requires a prototype-first
approach.
• Implies evolving the prototype!
• The specification slides to the
background.
20 Photo by Mark Konig on Unsplash
Flipping it around
• It is easier to use validations, data
structures or types that can’t be
represented in the specification.
• Focus shifts easily to the
implementation instead of the
API.
• API discussions may turn into
coding arguments.
21
Photo by Huey Images on Unsplash
Flipping it around
• It is easier to forget to add
annotations to the code.
• Details become absent from the
specification.
• The specification loses value.
22 Photo by Peter Pryharski on Unsplash
Now what?
Photo by Tom Ramalho on Unsplash
Key takeaways
24
Build
Build the OpenAPI file first
• Place all possible validations in the
OpenAPI file.
Generate
Generate code second
• API model DTOs are generated.
• Controller interface(s) are generated.
• Validations are translated into bean
validations.
• Default implementation ready with an
HTTP 501 response.
Extend
Extend the interface(s) with
the real controller(s).
Thank you!
Photo by Camylla Battani on Unsplash
linkedin.com/in/vascoveloso
@vveloso

API-first development

  • 1.
    API-First development From specification tocode and back Photo by Edho Pratama on Unsplash
  • 2.
    Your host today •Vasco Veloso • Working in software development since 1996. • Currently interested in designing software systems and giving back to the community. • Author and speaker. • Portuguese living in the Netherlands. linkedin.com/in/vascoveloso @vveloso
  • 3.
    API-first design The interfacespecification comes first. • Dependencies are discovered. • Data flows become apparent. • Data needs can be challenged. • Great input for threat modelling sessions. 3 Photo by Med Badr Chemmaoui on Unsplash
  • 4.
    The problem… There aremany different ways to capture interface designs... … that are not technical in nature … … and all require further translation before implementation! 4
  • 5.
    The problem… Many APIspecifications have little value • The interface is not complete. • Examples are missing. • Data formats or constraints are not defined. • The specification is outdated. • Code has changed but the specification did not. • There is little connection with the implementation. 5 Photo by Brett Jordan on Unsplash
  • 6.
  • 7.
    7 API-first development The interfacespecification comes first. Share Control Review Stakeholders CI/CD
  • 8.
    8 API-first development Capability enabler cf.2019 Accelerate State of DevOps Report (DORA) ✅ ✅
  • 9.
    9 API-first development Teams withquality documentation are… cf. 2020 Accelerate State of DevOps Report (DORA)
  • 10.
    API-first development The interfacespecification needs to be complete. • All inputs are present. • Data is properly described. • Descriptions and examples are included. Code needs to be generated from the specification. • Generate the server interface. • Generate client code. 10 Photo by Julia Joppien on Unsplash
  • 11.
    API-first development Advantages: • TheAPI specification is the single source of truth. • Code and specification are always in sync. • Specification is good enough to generate code? • Then it’s good enough to be used for testing! • Automatic implementation validation becomes possible. 11 Photo by Glenn Carstens-Peters on Unsplash
  • 12.
     OpenAPI plugin Restler  Schemathesis 12 API-first development High-level workflow for developing the server: Design API Write OpenAPI specification Generate code Implement & test business rules Deploy Workflow Refinement Validate implementation
  • 13.
    Imagine a Java serviceusing Spring… Photo by Scott Graham on Unsplash
  • 14.
    … with twooperations … paths: '/hello': get: summary: 'Retrieve a greeting.' operationId: 'getHelloWorld' tags: - "helloWorld" responses: '200': description: 'OK: Returning a greeting.' schema: $ref: '#/definitions/Greeting' '500': description: 'Internal Server Error' schema: $ref: '#/definitions/ResponseError’ put: summary: 'Set the greeting.' operationId: 'putHelloWorld' parameters: - in: body name: greeting schema: $ref: '#/definitions/Greeting' required: true description: 'The new greeting definition.' tags: - "helloWorld" responses: '200': description: 'OK: The greeting was stored.' schema: $ref: '#/definitions/Greeting' '500': description: 'Internal Server Error' schema: $ref: '#/definitions/ResponseError'
  • 15.
    … and twoobject definitions … definitions: ResponseError: type: object properties: code: type: string example: 'E-1234' description: 'Internal error code.' required: - code Greeting: type: object properties: text: type: string pattern: '[0-9a-z]' maxLength: 100 example: 'Hello' description: 'The greeting text.' required: - text
  • 16.
    … using Mavenwith the OpenAPI plugin … <plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <version>5.1.0</version> <executions> <execution> <id>api-generation</id> <goals><goal>generate</goal></goals> <configuration> <inputSpec> ${project.basedir}/src/main/openapi/spec.yaml </inputSpec> <output>${project.basedir}/gensrc</output> <generatorName>spring</generatorName> <modelPackage>api.example.model</modelPackage> <apiPackage>api.example.resource</apiPackage> <configOptions> <interfaceOnly>true</interfaceOnly> <dateLibrary>java8</dateLibrary> <useTags>true</useTags> </configOptions> </configuration> </execution> </executions> </plugin>
  • 17.
    … generated aninterface definition … /** * PUT /hello : Set the greeting. * * @param greeting The new greeting definition. (required) * @return OK: The greeting was stored. (status code 200) * or Internal Server Error: something has gone wrong. (status code 500) */ @ApiOperation(value = "Set the greeting.", nickname = "putHelloWorld", notes = "", response = Greeting.class, tags={ "helloWorld", }) @ApiResponses(value = { @ApiResponse(code = 200, message = "OK: The greeting was stored.", response = Greeting.class), @ApiResponse(code = 500, message = "Internal Server Error: something has gone wrong.", response = ResponseError.class) }) @PutMapping( value = "/hello", produces = { "application/json" }, consumes = { "application/json" } ) default ResponseEntity<Greeting> putHelloWorld(@ApiParam(value = "The new greeting definition." ,required=true ) @Valid @RequestBody Greeting greeting) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); }
  • 18.
    … with objectsthat can be validated. public class Greeting { @JsonProperty("text") private String text; public Greeting text(String text) { this.text = text; return this; } /** * The greeting text. * @return text */ @ApiModelProperty(example = "Hello", required = true, value = "The greeting text.") @NotNull @Pattern(regexp="[0-9a-z]") @Size(max=100) public String getText() { return text; } public void setText(String text) { this.text = text; } }
  • 19.
    Flipping it around Photo byPersnickety Prints on Unsplash
  • 20.
    Flipping it around •Requires a prototype-first approach. • Implies evolving the prototype! • The specification slides to the background. 20 Photo by Mark Konig on Unsplash
  • 21.
    Flipping it around •It is easier to use validations, data structures or types that can’t be represented in the specification. • Focus shifts easily to the implementation instead of the API. • API discussions may turn into coding arguments. 21 Photo by Huey Images on Unsplash
  • 22.
    Flipping it around •It is easier to forget to add annotations to the code. • Details become absent from the specification. • The specification loses value. 22 Photo by Peter Pryharski on Unsplash
  • 23.
    Now what? Photo byTom Ramalho on Unsplash
  • 24.
    Key takeaways 24 Build Build theOpenAPI file first • Place all possible validations in the OpenAPI file. Generate Generate code second • API model DTOs are generated. • Controller interface(s) are generated. • Validations are translated into bean validations. • Default implementation ready with an HTTP 501 response. Extend Extend the interface(s) with the real controller(s).
  • 25.
    Thank you! Photo byCamylla Battani on Unsplash linkedin.com/in/vascoveloso @vveloso