How to Make Swagger Codegen Work for Your Team
Swagger Codegen, the open source API client code generator, can be an extremely powerful, timesaving collaborative tool for your team. And like most powerful tools, it may not perfectly fit your needs right out of the box. In order to really make Swagger Codegen work for you and your team, itâs helpful to understand how Swagger Codegen works. But, first you might be asking, why?
The Virtue of âLazinessâ
We will encourage you to develop the three great virtues of a programmer: laziness, impatience, and hubris.
In a discussion of code generation tools, Iâd be remiss to start without mentioning Larry Wallâs âthree virtues of a programmer.â There might be a case to be made for Swagger Codegen in all three of these virtues, but I think the strongest case can be made for laziness.
Laziness, in this context, is explained by Larry Wall in the glossary of his famous book Programming Perl as, âThe quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find usefulâŠâ
Now, when Larry Wall praises the virtue of laziness as a programmer, heâs not referring to cutting corners and doing lazy things like not writing unit tests. Larry Wallâs definition of laziness should in no way be taken as a defense of laziness in general. But if youâre doing âlazinessâ right, and hopefully after reading this article you will be, using Swagger Codegen should mean not just less energy expenditure for yourself, but for all the members of your team.
You donât need to write, maintain, or worry about the consistency of code that was generated automatically! You can save your labor and energy for tasks like writing blogs about code generation. đ
But wait, before we get to generating code out of thin air we have to talk about what we need to get there as a team.
The OpenAPI Specification
The OpenAPI Specification (formerly known as the Swagger Specification) is a âlanguage-agnostic interface to RESTful APIsâ that enables developers of all kinds, versed in different programming languages, to discuss REST APIs in a way everyone understands. The specification allows developers to create a contract that defines how the API will work and what it should do before anyone has written a single line of code. This allows developers who will create and maintain the API and their clients to agree on a very specific contract and to say âIf I post this body with these headers to this endpoint then I expect a response in this format.â
To take a specific example, I invite you to look at this OpenAPI Specification for a Pet Store app. The Pet Store example is one you will become very familiar with as you learn more about the OpenAPI Specification. This is the reference point every developer uses when they implement the code generation rules for every new language. Integration tests are always performed against OpenAPI specifications in the OpenAPI Initiativeâs Github Repo.
After a brief review of the `petstore.yaml` we can see that a simple, but complete, API has been defined. Based on the requirements defined in this specification we could implement a backend service that returns a list of all pets in the database, or writes a new pet to the database, that was posted to the API. On the frontend side, we could then build the models we need to interact with the API and implement the correct services we could use to call the endpoints to fetch new pets or post new ones.
We could do all of that⊠but as âlazyâ programmers we could use Swagger Codegen to do it instead!
Getting Started With Swagger Codegen
Before you can use Swagger Codegen youâll need to install it locally. There are many different ways to install and use Swagger Codegen. In order to have the maximum amount of control to modify the project to fit our needsâââand in order to follow along with this blogâââthe best way to get Swagger Codegen is to clone the whole repo: https://github.com/swagger-api/swagger-codegen. Once you have the project locally, youâll need to run mvn clean package. If this completes successfully, youâll see the swagger-codegen-cli.jar created in the directory modules/swagger-codegen-cli/target/.
NoteâââYou can find instructions for downloading and installing Maven here if the `mvn` command line tool is not available in your environment.
This .jar file is a command line tool that provides the only interface youâll need for using Swagger Codegen once youâre ready to generate code. Letâs take a simple example and generate the Swift client models for interfacing with the Pet Store API we looked at earlier.
Letâs create a clean working directory for our experiments:
mkdir ~/PetStoreApp; cd ~/PetStoreApp; open .;
Copy the swagger-codegen-cli.jar file into the PetStoreApp folder and download the petstore.yaml file to the same folder (right click the webpage in your browser > Save AsâŠ)
You should now have two files in your PetStoreApp folder.
Generating Code
In your terminal, ensure you are in the /PetStoreApp directory and run the following command:
java -jar swagger-codegen-cli.jar generate -i petstore.yaml -l swift4 -Dmodels
The whole breakdown of all the options you can use to generate code can be found here. You can also use the `help generate` command with the .jar file for additional details around this command. But for now, letâs break down the simple command above:
java -jar swagger-codegen-cli.jar
The java command line tool allows us to pass a Java ARchive (JAR) file and execute it in the command line. In this way we can run the Swagger Codegen command line tool.
generate
Generate is the command passed to the Swagger Codegen CLI tool. This is the main way to invoke the tool, everything else passed to the CLI tool are options that modify the execution of the generate command.
-i petstore.yaml
This is the input specification file. In this example weâve passed in the Pet Store API yaml file. Swagger Codegen will use this specification file to generate our code.
-l swift4
Here we specify that we want Swagger Codegen to generate client-side Swift code for our app. We specified Swift 4, but Swagger Codegen has support for Swift 2, and Swift 3 as well.
-Dmodels
This final option specifies that we only want to generate model files for our API. This includes the models defined in the âdefinitionsâ section of the OpenAPI Specification at the bottom of the specification. Swagger Codegen can generate mock implementations of network code for you on the client. To keep this blog simple, Iâll be restricting our discussion to the generated models only and leave the generated networking code for another blog. Maybe đ
After running the above command from the PetStoreApp directory, here is what you should see:
Note that the only files that Swagger Codegen has generated for us this time are model files. These are the model files defined in the `definitions`section of the `petstore.yaml` file we referenced in the generate command that was used to generate these models. As noted above, the `-Dmodels` flag we used is what signals to the Swagger Codegen Tool that we only want models generated. Generating models alone is a very powerful feature of Swagger Codegen because it allows us to do a few things.
- It allows us the freedom to use any networking library in our app. Swagger Codegen supports building all of the network request management code for RXSwift, Alamofire and PromiseKit.
- Generating models reduces the human error that could occur when defining shared models on the backend and frontend of a service. Even if you misspell a property name you can guarantee itâs misspelled in exactly the same way on both ends of the network request!
- If your app uses a lot of different models to communicate with your backend service, or if those models change often, you can significantly reduce the amount of time and effort required to maintain or to write those models from scratch. This is especially true if your frontend and backend services are implemented in different languages.
YAML to Swift
Swagger Codegen has created three new files for us based on the petstore.yaml file we passed in as our input specification. Letâs take a closer look at the Pet.swift file.
public struct Pet: Codable {
public var _id: Int64
public var name: String
public var tag: String?
public init(_id: Int64, name: String, tag: String?){
self._id = _id
self.name = name
self.tag = tag
}
public enum CodingKeys: String, CodingKey {
case _id = âidâ
case name
case tag
}
}
When we compare this generated Swift model to the original definition in the OpenAPI Specification weâll see the relationship clearly. The original OpenAPI Specification for this object looks like this:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Even though the format of the data is in a slightly different form, all of the information we need to build the Swift object (or an object in any other language) is present in the OpenAPI Specification. However, the OpenAPI Specification is not the data from which the Swift object is ultimately generated. There is an intermediate state that Swagger Codegen generates based on the OpenAPI Specification prior to creating an object in a particular language.
An Intermediate Representation
While still in the /PetStoreApp directory we can run a command similar to the one we just ran, but this time weâll pass a command to show us that intermediate state.
java -jar swagger-codegen-cli.jar generate -i petstore.yaml -l swift4 -DdebugModels
Passing the -DdebugModels flag will dump a lot of data to the terminal. In order to center yourself, you can search for this string in the terminal âimportPathâ : â/Models.Petâ
This is the intermediate data object generated to represent the Pet model from the above OpenAPI Specification. Youâll notice that this has the same information as the OpenAPI Specification, but in a slightly different format. This data has been formatted in a way that we could easily use to generate a Swift class. For example, youâll see one of the properties on this data object is an array of variables. Each of these variables is a property on our Pet class, and this object gives us lots of very specific information about these variables. Here is the data we can see about just the id property alone:
âŠ
âvarsâ : [ {
âbaseNameâ : âidâ,
âgetterâ : âgetIdâ,
âsetterâ : âsetIdâ,
âdatatypeâ : âInt64â,
âdatatypeWithEnumâ : âInt64â,
âdataFormatâ : âint64â,
ânameâ : â_idâ,
âdefaultValueWithParamâ : â = data.id;â,
âbaseTypeâ : âInt64â,
âjsonSchemaâ : â{\n \âtype\â : \âinteger\â,\n \âformat\â : \âint64\â\n}â,
âexclusiveMinimumâ : false,
âexclusiveMaximumâ : false,
âhasMoreâ : true,
ârequiredâ : true,
âsecondaryParamâ : false,
âhasMoreNonReadOnlyâ : true,
âisPrimitiveTypeâ : true,
âisContainerâ : false,
âisNotContainerâ : true,
âisStringâ : false,
âisNumericâ : true,
âisIntegerâ : false,
âisLongâ : true,
âisNumberâ : false,
âisFloatâ : false,
âisDoubleâ : false,
âisByteArrayâ : false,
âisBinaryâ : false,
âisFileâ : false,
âisBooleanâ : false,
âisDateâ : false,
âisDateTimeâ : false,
âisUuidâ : false,
âisListContainerâ : false,
âisMapContainerâ : false,
âisEnumâ : false,
âisReadOnlyâ : false,
âvendorExtensionsâ : {
âx-swift-optional-scalarâ : true,
âx-codegen-escaped-property-nameâ : true
},
âhasValidationâ : false,
âisInheritedâ : false,
ânameInCamelCaseâ : âIdâ
âisXmlAttributeâ : false,
âisXmlWrappedâ : false
}
âŠ
There is some magic going on here that some very helpful Swift developers have created for us. One particular example is the ânameâ property. Youâll notice that the ânameâ is â_idâ, but the âbaseNameâ is âidâ. This is helpful because id is a reserved keyword in Obj-C.
To make this generated code more interoperable with Obj-C, youâll notice if you scroll up, that even though our Pet model is defined as having a property name of âidâ, that case has been safely handled in the generated Swift class. That way, when we use the object on the client-side, weâll reference the property as â_idâ. However, anytime the object is serialized into JSON to be sent to the server, the correct âidâ property name will be used.
But there still seems to be something missing here⊠How do we go from an OpenAPI Specification, to this large and unwieldy JSON blob, to a perfectly rendered Swift class? The answer is mustache templating!
Mustache Templating
The mustache templating system is used to insert variable values into a static text template. In the simplest possible example from the Mustache Documentation, itâs clear how the mustache templating system gets its name. The myriad sideways mustache-looking curly braces are used to identify the variable value that should be replaced.
Template:
{{#person?}}
Hi {{name}}!
{{/person?}}
Hash:
{
âperson?â: { ânameâ: âJonâ }
}
Output:
Hi Jon!
Using this same system, we not only can generate models for a mobile app written in Swift, but we can generate code for every language we could possibly want. Sticking with the Swift example a bit longer, letâs investigate how you might create a template for a Swift object using the mustache template system.
Try it Yourself!
The Swagger Codegen implementation of a Swift 4 model object can be found here. But letâs make a simple version to illustrate some of the basics:
Template:
import Foundation
public class {{classname}}: Codable {
{{#vars}}
public var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}
{{/vars}}
init({{#vars}}{{name}}: {{{datatype}}}{{^required}}?{{/required}}{{^isFinal}},{{/isFinal}} {{/vars}}) {
{{#vars}}
self.{{name}} = {{name}}
{{/vars}}
}
}
Hash:
{
âclassnameâ: âPetâ,
âvarsâ: [
{
ânameâ: â_idâ,
âdatatypeâ: âInt64â,
ârequiredâ: true,
âdefaultValueâ: 1
},
{
ânameâ: ânameâ,
âdatatypeâ: âStringâ,
ârequiredâ: false,
âisFinalâ: true
}
]
}
Output:
import Foundation
public class Pet: Codable {
public var _id: Int64 = 1public var name: String?
init(id: Int64, name: String? ) {
self._id = _id
self.name = name
}
}
I suggest taking the above template and JSON hash and inserting them into the simple Mustache demo app. Here you can play around with modifying the values of the hash or alter the mustache template to see if you can improve on, or create, new functionality. How could you re-create the coding keys enum seen in the Swift object at start of this blog? How could you modify the mustache template so that all of the data models created with this template conformed to the equatable or hashable protocols? If you can answer those questions then youâre well on the way to being the kind of âlazyâ programmer who might never need to write or update another data model ever again!
Standard Swagger Codegen Customization
Most users of Swagger Codegen never need to modify the mustache templates that underlie the Swagger Codegen library itself as most use cases have been covered by the default implementation of each languageâs mustache templates. However, there are also other ways to modify the output of Swagger Codegen without ever needing to modify the mustache templates. The default way to modify the behavior of the different language templates is to pass in a configuration object.
You can find out what configuration properties each language supports with this command:
java -jar swagger-codegen-cli.jar config-help -l
Optionally use libraries to manage response. Currently PromiseKit, RxSwift are available.
responseAs
Optionally use libraries to manage response. Currently PromiseKit, RxSwift are available.
unwrapRequired
Treat ârequiredâ properties in response as non-optional (which would crash the app if API returns null as opposed to required option specified in json schema
```
To use these properties, youâll need to create a new file where you can create a JSON object to pass as a new parameter to the Swagger Codegen CLI tool. While still in the PetStoreApp folder you created earlier, you can run the following command to create a JSON object that specifies RxSwift as the library for handling HTTP responses:
echo {âresponseAsâ:âRxSwiftâ} > config.json
Now you can pass this config.json file into the original CLI command to generate our code, while removing the directive -Dmodels and allowing Swagger Codegen to generate the full range of files beyond just data models:
java -jar swagger-codegen-cli.jar generate -i petstore.yaml -l swift4 -c config.json
Conclusion
Now you should have a pretty good understanding of not just what Swagger Codegen tools can do, but how to use them to embody the virtue of âlazinessâ. However, weâve still only barely scratched the surface of the amazingly powerful tool that is Swagger Codegen and how it can help you reduce overall energy expenditure ala Larry Wall.
Swagger Codegen is under extremely active development and has an involved community so definitely check out their Github page for the latest on Swagger Codegen. Also, there are some other great codegen tools out there in addition to Swagger Codegen. Iâd love to hear about other tools you like or your experiences with them. Feel free to reach out to me on Twitter for any questions or comments.