11436 SSO

Apigee-127 Tutorial: Implementing API Controllers

jwest
Nov 12, 2014

Apigee-127 provides a quick and easy way to build great APIs. The general approach is:

  1. Model your API using Swagger 2.0 and the Swagger editor

  2. Map paths and operations to Node.js controllers and methods

  3. Implement your API in the Node.js controllers (or map your path/operations to existing modules)

This post is part of a continuing series on Apigee-127.  Here we’ll provide an overview of how it handles controller/method mappings.

When you build APIs with Apigee-127, the “controller” files are where you put the business logic associated with your API. These are Node.js files that leverage JavaScript to execute logic. Recently, one of our customers told us: “With 127 I’ve been able to do something in two weeks that I should not be able to do! I will demo this to developers in my company tomorrow and make their jaws drop—and I’m not a developer! It’s in JavaScript and its so easy to build APIs!”  To a product manager, this type of feedback is very gratifying.

Using the JavaScript language to build APIs makes it easy for web developers, backend developers, and non-developers to build APIs—and not just simple APIs. With Swagger annotations, you can build APIs that are cached, secured with OAuth 2.0, managed with quotas, protected with spike arrest policies and collect analytics on those APIs without writing a single line of code to use these policies.

For Apigee-127, these are table stakes. You can build elegant, enterprise-class APIs in Node.js with a few simple lines of JavaScript code. But what is the next level down? What about “complex” APIs that need custom caching or custom datasources? Here we’ll show you how to build both simple and complex controllers.

Controller basics: controller mapping

In order for controllers to be found and used by Apigee-127, they must be associated with a path in your Swagger spec using the x-swagger-router-controller annotation. If there is no x-swagger-controller-annotation for a path::operation then no controller mapping will be made.

The name of the controller used will tell Apigee-127 the name of the file to look for in {project}/api/controllers for the business logic associated with the path. Take the following example:


{project}/api/swagger/swagger.yaml:
 /weather:
   x-swagger-router-controller: weather
   get:
     ...
   put:
     ...

Here the x-swagger-router-controller has been specified as “weather.”  This will result in Apigee-127 looking for the controller for /weather file at the path {project}/api/controllers/weather.js.

The x-swagger-router-controller annotation can also be used at an operation level.  This can be used in conjunction with, or instead of x-swagger-router-controller being used at the path level. Take the following example:


{project}/api/swagger/swagger.yaml:

 /weather:
   x-swagger-router-controller: weather
   get:
     x-swagger-router-controller: weather_read
   put:
     …
   delete:
     …

Here the x-swagger-router-controller has been specified as “weather” for the /weather path but for the GET verb the controller is specified as weather_read.  In this case the {project}/apis/controllers/weather_read.js would be used for GETs but all other operations will use {project}/apis/controllers/weather.js.

Controller basics: operation mapping

There are two ways that a PATH+VERB combination in Swagger gets mapped to a controller method: explicitly via the operation and implicitly via the associated HTTP verb.

Explicit operation mapping

If the operationId attribute is used on the operation (VERB) in the Swagger spec then Swagger Tools will look for the {controller-name}.{operationId} method for the logic associated with the operation. Take the following example:


{project}/api/swagger/swagger.yaml:

 /weather:   x-swagger-router-controller: weather
   get:
     operationId: getWeatherByCity


 


{project}/api/controllers/weather.js:


module.exports = {
 getWeatherByCity: getWeatherByCity
}

function getWeatherByCity(req, res) {
 var city = req.swagger.params.city.value;
 var url = "http://api.openweathermap.org/data/2.5/weather?q="+city+"&units=imperial";
 console.log('Executing request: '+url);
 request.get(url).pipe(res);
 };

In the example above, the x-swagger-router-controller has been specified as “weather” and the operationId has been specified as ‘getWeatherByCity’. This will result in Apigee-127 looking for the controller file at the path {project}/api/controllers/weather.js.  The method of the controller that will be used for GET on /weather would be getWeatherByCity (which would need to be defined in the module.exports of the controller).

Implicit operation mapping

If the operationId attribute is not specified for the operation, then Swagger tools will look for the {controller-name}.{VERB} method for the logic associated with the operation. Take the following example:


{project}/api/swagger/swagger.yaml:

 /weather:
   x-swagger-router-controller: weather
   get:
     … (no operationId)

 


{project}/api/controllers/weather.js:

module.exports = {
 get: get
}

function get(req, res) {
 var city = req.swagger.params.city.value;
 var url = "http://api.openweathermap.org/data/2.5/weather?q="+city+"&units=imperial";
 console.log('Executing request: '+url);
 request.get(url).pipe(res);

 };

In the example above the x-swagger-router-controller has been specified as “weather.”  If an operationId is not specified, then the HTTP verb would be used—in this case “get.” You also have the option of simply using the HTTP verb name in the module.exports and mapping “get” to ‘getWeatherByCity’ as follows:


{project}/api/controllers/weather.js:

module.exports = {
 get: getWeatherByCity
}

function getWeatherByCity(req, res) {
 var city = req.swagger.params.city.value;
 var url = "http://api.openweathermap.org/data/2.5/weather?q="+city+"&units=imperial";
 console.log('Executing request: '+url);
 request.get(url).pipe(res);
 };

Apigee-127 gives you options to take a Swagger spec and wire the implementation for your API with a few simple annotations. You can take the default convention of the HTTP verb as the method name, which gets bound to a path+operation or you can specify a specific operation name using the operationId.

In the next post we’ll cover how you can add caching to an endpoint with a few lines of markup in your Swagger spec.  Stay tuned and let us know if you have any questions or issues!
 
 

Microservices Done Right

Next Steps

 
 

Resources Gallery

News