The OpenAPI Spec and Kitura: Getting Started

Get started with the OpenAPI spec in this server-side Swift tutorial on using the Swagger API with Kitura to generate an SDK for your iOS app! By David Okun.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Examining Your Specification

Start by looking at the very top of your JSON spec:

{
"schemes" : [
  "http"
],
"swagger" : "2.0",
"info" : {
  "version" : "1.0",
  "title" : "Kitura Project",
  "description" : "Generated by Kitura"
},

This is metadata describing your project. Consider that one purpose of this specification is to allow other people to easily understand your API’s structure and purpose. This is where human-readable metadata comes in handy.

Right after this metadata, you’ll find the “paths” node. This contains an array of “/entries” describing each of the endpoints you’ve created so far. Find the “get” entry within this:

"get": {
  "responses": {
      "200": {
         "schema": {
           "type": "array",
              "items": {
                "$ref": "#\/definitions\/JournalEntry"
              }
            },
       "description": "successful response"
     }
  },
  "consumes": [
    "application\/json"
  ],
  "produces": [
    "application\/json"
  ]
}

The top of this snippet indicates that you are looking at a GET route for the path “/entries”. Three sub-nodes describe this route:

  • “responses” provides an array of responses a user can receive.
  • “consumes” specifies the data type this method must be given.
  • “produces” describes the type of data the response will contain.

Next, still within the “/entries” node, find your POST route. Note that in comparison to the GET route you just saw, this contains an additional sub-node:

"parameters" : [
  {
    "in" : "body",
    "name" : "input",
    "required" : true,
    "schema" : {
      "$ref" : "#\/definitions\/JournalEntry"
    }
  }
]

As you can see, when you write a POST route, the spec generator automatically adds information about the body of the request you need to send to yield a successful response.

Take a moment to scroll through the rest of your currently available methods and take note of the similar properties and formats shared across each method.

You’re seeing the consistent sets of data defined by the OpenAPI Specification that will enable the generated UI to test each method you write. Now, scroll down to the “definitions” node:

"definitions": {
  "Status": {
    "type": "object",
    "required": ["timestamp","details","status"],
    "properties": {
      "status": {"type":"string"},
      "details": {"items":{"type":"string"},"type":"array"},
      "timestamp": {"type":"string"}
    }
  },
  "JournalEntry": {
    "type": "object",
    "required": ["emoji","date"],
    "properties": {
      "id": {"type":"string"},
      "emoji": {"type":"string"},
      "date": {"type":"number"}
    }
  }
}

Focus on the JournalEntry node, here. You have a Swift model object named JournalEntry in Source/Models that is representable as JSON. The KituraOpenAPI module has automatically added it to your project spec!

This is a nice demonstration of how the KituraOpenAPI module dynamically observes the types of your JournalEntry object and automatically constructs a reference for them in your specification.

Here’s the really great thing about all this: You now have living, breathing documentation of your API. As you continue to build out your app, be sure to check back and see how this specification grows and changes automagically!

Using the Kitura OpenAPI UI

Make sure your EmojiJournalServer app is still running in Xcode, then open a browser and go to http://localhost:8080/openapi/ui. You should see a page like this:

OpenAPI UI

You’ve just learned how this information is created under the hood. Now it’s time to learn how to explore and interact with your API.

Click on the POST route. Notice how the route’s description expands:

POST route

Each component of your JSON is clearly represented, here. Even better, your entire API is now fully explorable!

Click the Try it out button at the top of this expanded page.

Because a POST route requires input, the interface pauses to let you enter this, displaying the input types required.

In the main text area (underneath the Edit Value | Model caption), enter the following:

{
  "emoji": "🤓",
  "date": 563725805.57661498
}

Before continuing, two notes about this:

  1. To bring up the emoji picker on a macOS machine, press Control + Command + Space bar.
  2. The date here is represented as a number that doesn’t really seem to mean anything. Don’t worry — this is just how Swift stores raw Date values under the hood. As is, this will work fine with your iOS app. It’s also possible to add a custom decoder so you can pass and view Date values as ISO8601 strings, instead of raw values.

Click the Execute button, and note how the UI updates:

Executing the POST

Remember that inscrutable cURL command you’ve been using? This page shows you the exact cURL syntax needed to test any route you select! This can be very helpful in cases where you need to do a quick and dirty test — or if you just can’t convince a stubborn teammate how useful this module really is!

Also, notice that you received a “201” response and that the object you created is echoed back to you for your inspection. As expected, your POST route works! You can also easily see the headers of your response, which can be very helpful if you run into issues later on.

Note: Under the Server response header is the actual response you received from trying out the API. Underneath that is a “Responses” header that was part of the documentation before you tested out the API. One is the actual response, the other the documentation of what a response should look like. Hopefully this helps you understand the difference.

Now, scroll to your GET /entries route. Take a moment to familiarize yourself with the contents of the page, then click Try it out and then Execute:

Executing the GET

As expected, your GET route returns the JournalEntry object that you created with your POST request. You also get other key information returned with that response in an easily digestible UI. Is this UI nice or what?

Feel free to test the rest of the Kitura OpenAPI UI at your leisure. Now it’s time to give your iOS app a little love!

Generating an SDK for Your iOS App

The last thing you’re going to do with the OpenAPI Specification is so awesome that it just might become your very favorite!

One of the auxiliary toolsets within the OpenAPI Initiative is the swagger-codegen tool, which lets you generate a client SDK in up to 30 different programming languages! I encourage you to play around with this tool and try generating some other SDK’s in other languages (we’ll stick with Swift, here).

For the last part of this tutorial, you’ll need to make sure that your system has Docker up and running. The instructions in the tutorial Docker on macOS: Getting Started should be enough to get you going with Docker. To ensure that you are ready to go, open Terminal and type in docker version. If you see output telling you that you are running version 18.09.0 or better, then you’re all set.

Note: If you’d rather install this codegen tool through Homebrew — you can! We’ve chosen not to go that route in this tutorial so that you don’t need to install Java (a requirement of the codegen tool) directly onto your machine, since Java can be a bit of a lightning rod for many developers. By installing codegen in Docker, you’re shielded from having to worry about any potential harms of Java.

In Terminal, type the following command:

docker pull swaggerapi/swagger-codegen-cli

Once the image and all of its dependencies are done installing on your machine, enter this command in Terminal:

docker run --rm -v ${PWD}:/local \
  swaggerapi/swagger-codegen-cli langs

If you are somewhat familiar with Docker, then you may notice that you are specifying a volume to mount into this image. This is so that the codegen tool has a reference back to your local directory when it takes the parameters it needs. The output of this command should look like similar to this:

Available languages: [ada, ada-server, akka-scala, android, apache2, apex, aspnetcore, bash, csharp, clojure, cwiki, cpprest, csharp-dotnet2, dart, elixir, elm, eiffel, erlang-client, erlang-server, finch, flash, python-flask, go, go-server, groovy, haskell-http-client, haskell, jmeter, jaxrs-cxf-client, jaxrs-cxf, java, inflector, jaxrs-cxf-cdi, jaxrs-spec, jaxrs, msf4j, java-pkmst, java-play-framework, jaxrs-resteasy-eap, jaxrs-resteasy, javascript, javascript-closure-angular, java-vertx, kotlin, lua, lumen, nancyfx, nodejs-server, objc, perl, php, powershell, pistache-server, python, qt5cpp, r, rails5, restbed, ruby, rust, rust-server, scala, scala-gatling, scala-lagom-server, scalatra, scalaz, php-silex, sinatra, slim, spring, dynamic-html, html2, html, swagger, swagger-yaml, swift4, swift3, swift, php-symfony, tizen, typescript-aurelia, typescript-angular, typescript-inversify, typescript-angularjs, typescript-fetch, typescript-jquery, typescript-node, undertow, ze-ph, kotlin-server]

The starter project directory for this tutorial has an EmojiJournalMobileApp directory to get you started. Still in Terminal, navigate to the root folder of EmojiJournalMobileApp. Create a new directory to store your generated SDK, and a local file for your specification:

mkdir GeneratedSDK
touch specification.json

Even though the specification is live for you at http://localhost:8080/openapi, the Docker container you are about to run can’t easily access that URL outside of its own environment. You could monkey around with container network settings, but for simplicity you’ll take a simpler approach.

Open specification.json in a text editor. Copy the contents of http://localhost:8080/openapi from your browser and paste them into specification.json. Save your file.

Here comes the magic! Enter the following command:

docker run --rm -v ${PWD}:/local \
 swaggerapi/swagger-codegen-cli generate \
 -i /local/specification.json -l swift \
 -o /local/GeneratedSDK

Open your GeneratedSDK folder and take a look at its newly created contents:

GeneratedSDK folder

With a single CLI command, you’ve just created a fully functional Swift SDK that can handle all of your network communications with this specific server. Think of the time you’ll save with all the code you will no longer have to write!

We’re big fans of small commands that yield big output, and this is an especially sweet example of that! Let’s take a moment to understand what the codegen tool has given you in a bit more detail:

  1. The underlying HTTP request to generate your SDK is made via Alamofire, an extremely popular Swift networking library by Matt Thompson.
  2. You get both Cartfile and .podspec files to implement this SDK. This means you can choose either Carthage or Cocoapods to integrate your SDK. Either way, you won’t have to drag and drop all the files into your project over and over!
  3. Everything is documented!

Open up GeneratedSDK/SwaggerClient/Classes/Swaggers/APIs/DefaultAPI.swift in a text editor. Locate the method entriesPost — line 93 at the time this was written:

public class func entriesPost(input input: JournalEntry, 
  completion: ((data: JournalEntry?, error: ErrorType?) 
  -> Void)) {
    entriesPostWithRequestBuilder(input: input)
      .execute { (response, error) -> Void in
        completion(data: response?.body, error: error);
    }
}

This method is your entry point to sending a new JournalEntry to your server! This means that you can use the following code to create a new JournalEntry object and send it to your server:

let newEntry = JournalEntry(id: nil, emoji: "😍", date: Date())
DefaultAPI.entriesPost(input: newEntry) { data, error in
  // handle response here
})

Nice and simple! This gives you a quick sample of how useful codegen can be as you develop your Kitura apps. You’ll likely find that being able to automatically generate client APIs — in a very wide array of languages and directly from your server code — will be incredibly useful. In practice, this can not only save time and get you up and running quickly, it can also save errors and prove especially helpful for folks who aren’t (gasp) working in Swift!