The Perfect REST API (Part 2)

In the last post of this series we had a brief introduction into Swagger and the principles of RESTful API design. This post will dive into the technical details of a Swagger integration into the development process of our API back end. I will assume, that you know the basics of Node.js and how to use the Node.js package manager npm.

To use the ideas and concepts of that blog post for your own project, I would recommend to have a closer look at these two npm packages:

Let Swagger do the heavy lifting

In the first post you have heard a lot about the Swagger YAML file defining both the available routes of the API and the necessary definitions of the objects being handled by the API.

Now it's time to use the YAML file and feed it to Swagger to let Swagger create the actual routes in an Express.js based back end application. Then we only have to implement the logic that is necessary for each route and let Swagger do the wiring between the different parts.

Before Swagger can do the wiring, it must know which route will be associated with which function in which controller. Therefor we use specific keywords in the Swagger YAML file to add the required information.

/camels:
    x-swagger-router-controller: camelController
    get:
      description: Returns a list of camels.
      operationId: getListOfCamels
      tags:
        - Camels
      responses:
        "200":
          description: Camel array
          schema:
            type: array
            items:
              $ref: "#/definitions/Camel" 

The x-swagger-router-controller specifies the file name where the function specified in operationId is defined. So now Swagger knows where to look for the function that is getting called when the front end accesses the /camels route.

That's it for the theory. Let's have a look at the concrete project structure that is necessary to make the above description work.

Creating the project structure

The project structure heavily depends on the configuration of your swagger configuration file. I recommend the following structure since it is the standard project layout when using the Swagger command line tool and it has proven successful in several projects.

/api
    /controllers
    /helpers
    /mocks
    /swagger
/config
/test

You can get that structure created with the Swagger CLI by issuing the following command:

swagger project create your-project-name  

In the app.js file that is used to start the whole back end API application I would recommend to replace the swagger-express-mw package by the swagger-node-runnerpackage, since the first one caused a lot of headache in various environments for me. Do not forget to install the swagger-node-runner package, if you did not do so already. Your app.js would then look like:

var express = require('express');  
var runner = require('swagger-node-runner');

var config = {  
  appRoot: __dirname
};

runner.create(config, function(err, swaggerRunner) {  
  if (err) { throw err; }

  var expressRunner = swaggerRunner.expressMiddleware();
  expressRunner.register(express());

  app.listen(80);
});

When you start your application with that setup, it will use the Swagger middleware to create the API routes and wire them with the controllers' logic to provide the functionality you have defined in your Swagger YAML file.

Using Typescript to define the API objects

As you already know if you read the first post of this series, the Swagger definition file contains a definition section for the API objects, so that everybody working with the API knows about the structure of the different objects.

If you only keep your structure in that Swagger definition file, it will create a big hassle to transform those object definitions into data structures that you can use for the coding process of both front end and back end. Instead of replicating those definitions manually, we could use the handy npm package called typson to write our object definitions in Typescript and derive the Swagger definitions from those Typescript definitions. The advantage: You can use type information in the development process and automatically keep the Swagger definitions in sync with the actual implementation.

The process is simple. Include the typson package in your gulpfile.js and use a gulp task to transpile all Typescript definitions into a YAML file comprising the Swagger definitions.

gulp.task('typson', function() {  
  gulp.src('./api/contracts/contracts.ts')
    .pipe(data(function(file) {
      return typson.schema(file.path)
        .done(function (schema) {
          fs.writeFileSync('./api/swagger/definitions.yaml', yaml.stringify(schema, 10, 2));
        });
    }));
});

To give you an example of such a Typescript-to-Swagger transformation, I wrote the Camel example object.

/**
 * Data structure of a camel.
 */
export interface Camel {

  /** Unique identifier for the camel */
  id: number;

  /** Name of the camel */
  name: string;

  /** Color of the camel */
  color?: string;
}

That will translate into

Camel:  
    id: Camel
    type: object
    description: 'Data structure of a camel.'
    properties:
      id:
        description: 'Unique identifier for the camel'
        type: string
      name:
        description: 'Name of the camel'
        type: string
      color:
        description: 'Color of the camel'
        type: string    
    required:
      - code
      - name
    additionalProperties: false

Now you could either append the freshly created definitions to the Swagger YAML file manually or you have the definitions file being exposed by some local web server. When you e.g. expose your definitions.yaml file on http://localhost:8080 you could reference it in your Swagger.yaml file as follows:

$ref: "http://localhost:8080/definitions.yaml#/definitions/Camel"

Bringing it all together

Today you learned how to integrate the Swagger eco-system in your back end development process. You can now use your Typescript interface definitions as Swagger definitions and have the Swagger paths being automatically created as Express.js routes.

At some point I will add an example project on Github, that will help you getting started. If I forget about that, just send me an email and remind me of that outstanding promise :-)

In the next post we will have a look at the front end integration part. We will see, how we can auto-generate front end mocks for all existing routes and how we can implement custom mock objects, if we need them.

Stay tuned!