Skip to content
This repository has been archived by the owner on Jul 14, 2022. It is now read-only.

tutorial quota

wwitman edited this page Apr 24, 2015 · 80 revisions

Tutorial: Getting started with a127

This tutorial walks you through the basic development pattern for an a127 API project. It takes around 20 minutes to complete. When you finish, you'll understand:

  • How to create a new a127 project
  • How an a127 project is structured
  • How to validate your project's structure
  • How to create an a127 "account"
  • How to edit a project's Swagger definition file
  • How to declare and apply a quota policy to an API path
  • How to write a simple Node.js controller function
  • How to write a simple Node.js helper function
  • How to deploy a project to platforms like Apigee Edge and AWS Elastic Beanstalk
  • How to create and use a RemoteProxy "service"

The patterns you'll see in this tutorial can be applied to many different use cases as you begin developing more complex API projects. For example, when you complete this tutorial, you will be able to configure and use other kinds of API policies, like spike arrest, cache, and OAuth, with confidence.

Prerequisites

We recommend that you:

  • Complete the Quick start steps. It only takes a few minutes to install a127 and get your first "hello world" API up and running. From this point on, we assume that you've done those steps.

  • Be familiar with JavaScript and Node.js

  • Install the latest version of a127:

    npm install -g apigee-127

Get started

Okay, now let's create an API using a127. It'll be a simple API, but more interesting than 'hello world'. This API returns weather data from a cloud-based weather service called OpenWeatherMap. Our API lets you specify a city name and returns the weather for that city.

  1. In a new or temp directory, create an a127 project called quota-tutorial:

    a127 project create quota-tutorial

  2. CD to the new project directory:

    cd quota-tutorial

  3. In a text editor, open the package.json file in the project root directory, and add the request.js module to the list of dependencies. We'll use this module when we implement the project's controller.

        "dependencies": {
            "request": ""
            ...
    
  4. In the project root directory, run npm install to pick up the module.

  5. Open the project's Swagger definition file in the Swagger editor:

    a127 project edit

    Note: This Swagger 2.0-compliant file resides in quota-tutorial/api/swagger/swagger.yaml in your project structure. It defines the structure of your API. It is written in the easily readable YAML format and surfaces the design of the API in an intuitive manner, including API routes, parameters, operations, response structures, and so on.

    alt text

  6. Replace the entire paths block in the Swagger file with the following block. This new block describes our simple API. It has one path, /weather and it takes one query parameter called city.

        paths:
          /weather:
            x-swagger-router-controller: weather
            get:
              description: "Returns current weather in the specified city to the caller"
              operationId: getWeatherByCity
              parameters:
                - name: city
                  in: query
                  description: "The city you want weather for in the form city,state,country"
                  required: true
                  type: "string"
              responses:
                "200":
                  description: "Success"
                  schema:
                    $ref: "#/definitions/HelloWorldResponse"
                default:
                  description: "Error"
                  schema:
                    $ref: "#/definitions/ErrorResponse"

Note: Take note of the values for x-swagger-router-controller and operationId. The "controller" is a Node.js file that you'll create next, and the "operationId" is the name of a function that is exported from that file.

  1. When you make the change in the Editor, the file is automatically saved. If you introduce any errors, you'll have to fix them. The editor is very picky about indentation.

  2. Tip: Use the a127 project verify command to validate your project's Swagger file and configuration. This command is useful if you choose not to use the Swagger Editor to edit the Swagger file:

    cd quota-tutorial
    a127 project verify
    Results: 0 errors, 0 warnings
    
  3. In a text editor, create this new file: <project-root-folder>/api/controllers/weather.js. The name of this file (minus the .js) is specified in x-swagger-router-controller element in the Swagger file.

  4. Paste this Node.js code into the file. The getWeatherByCity() function executes whenever the /weather API path is called. This method is specified in the Swagger file as the operationId on the /weather path.

        'use strict';
    
        var util = require('util');
        var request = require('request');
    
        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);
          };

    Note: The getWeatherByCity() function uses the request.js module to make a callout to the backend weather service. Essentially, your a127 API is a proxy for that backend API. As we'll see, one advantage of using a127 is the ability to add policies and other features to the API proxy.

  5. Save the file.

  6. Start the project.

    cd quota-tutorial
    a127 project start

    Note: This command starts the main Node.js file /app.js. In another terminal window, open this file and take a look at it. It creates the project server, adds middleware, and prints a help message. Feel free to change the help message to reflect how you call this API (shown in the next step).

  7. Test the API. It'll return weather data for the specified city.

    curl http://127.0.0.1:10010/weather?city=Hanover,PA
    
    {"coord":{"lon":-76.98,"lat":39.8},"sys":{"message":0.011,"country":"United States of America","sunrise":1429525334,"sunset":1429573876},"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"02d"}],"base":"stations","main":{"temp":75.75,"temp_min":75.75,"temp_max":75.75,"pressure":995.57,"sea_level":1015.6,"grnd_level":995.57,"humidity":68},"wind":{"speed":10.32,"deg":200.5},"clouds":{"all":8},"dt":1429549449,"id":4558475,"name":"Hanover","cod":200}

You now have a working weather API. You've seen some important parts of an a127 project. Let's review them:

  • Swagger file -- The Swagger file is where you define the structure of your API: its paths, policies, responses, parameters, etc. It resides in <project-root>/api/swagger/swagger.yaml.
  • Swagger editor -- An interactive editor for Swagger-compliant files. It checks for errors and automatically generates API documentation as you work.
  • controller file -- Controllers are where you implement the operational logic for your API paths in Node.js. In this example, the controller makes a callout to a public weather API and returns the result in the response.
  • A127 project structure -- You've seen the basic file structure -- the location of the Swagger file, controller files, package.json, main Node.js file, and so on.
  • a127 CLI -- You've used several a127 command line commands to create, edit, and start a project. For the complete list, see a127 CLI reference.

Next, we'll show you how to add a quota policy to the API.

Add a quota policy

A quota policy limits the number of API requests in a given interval of time. To add this policy, the first thing you need to do is declare it in the x-127-services part of the Swagger file.

  1. From the project root folder, open the Swagger editor again (if you previously closed it):

    a127 project edit
  2. Add the following quota policy block under x-a127-services, like this. In this context, the term "services" and "policies" are equivalent. You declare policies in the x-a127-services section of the Swagger file.

    Note: Here, we are simply declaring a quota policy, which we will apply to an API path later. Correct any YAML errors in the editor, if necessary. The editor is very picky about indentation.

        x-a127-services:
          ## Add the quota policy
          quota:
            provider: "volos-quota-memory"
            options:
              timeUnit: "minute"
              interval: 1
              allow: 3

Note: The options configure the behavior of the quota policy. In this case, the quota policy is exceeded if more than three API calls occur in a one-minute interval.

  1. Now that quota is declared, we're going to apply the policy to the /weather path with the x-a127-apply element. Place the element just below x-swagger-router-controller as shown below. Whenever the /weather path is called, the policy will be executed.

        paths:
          /weather:
            x-swagger-router-controller: weather
            x-a127-apply:
                quota: {}
            get:
               ...
               *** Note: everything below "get:"" stays the same. Do not make any other changes *** 
    
  2. Open the file ./config/default.yaml in a text editor.

  3. Change the validateResponse attribute to false and save the file.

    validateResponse: false
    

Note: We'll cover response validation in another topic. For now, it's not necessary to enable it.

  1. Start the project.

    cd quota-tutorial
    a127 project start
  2. Call the API several times in quick succession. After the third try, the quota policy kicks in and returns a 403 error, as shown below:

    curl http://127.0.0.1:10010/weather?city=Hanover,PA
    
    {"message":"exceeded quota","status":403}

You've taken an important step by adding a policy to your API. First, we declared it and then we applied it to a path. Other a127 policies include spike arrest, OAuth, API key, basic authentication, cache, and analytics. The pattern for adding them is the same as for quota. For more information, see Introduction to policies.

Next, we'll refine our quota example by adding a "helper function" that sets the quota key dynamically based on the IP address of the incoming request.

Use a helper function

Helper functions are called before controllers, and are usually used to set policy values dynamically.

  1. CD to your project's root folder.

  2. Using a text editor, create a file called ./api/helpers/quota.js.

  3. Paste this Node.js code into the file and save the file.

    Note: The file exports the clientIp() function, which sets the quota key to the value of the IP address from which the API call originated. This pattern lets you apply quotas selectively. In the next step, we'll wire that function into the Swagger file.

        'use strict';
    
        var debug = require('helpers');
    
        module.exports = {
          clientIp: clientIp
        };
    
        function clientIp(req) {
          var key = req.connection.remoteAddress;
          console.log('clientIp Key: ' + key);
          return key;
        }
  4. We're requiring the "helpers" module in our helper file. Add it to the package.json file in your project's root directory:

        "dependencies": {
            "helpers": "",
            ...
    
  5. Execute: npm install to pick up the module.

  6. From the project root folder, open the Swagger editor again.

    a127 project edit
  7. Remove the x-a127-apply block in the /weather path that includes the quota policy, and replace it with this new block.

    Note: We're adding a "key" attribute to the quota. The attribute's value is set by the clientIp function in the quota.js helper file. The function is called whenever an API request is received, and executes before any controller code executes. (Note the .js isn't required. Just referring to the helper file as quota is sufficient.)

        x-a127-apply:
            quota:
              key:
                helper: quota   # This is the api/helpers/quota.js file. 
                function: clientIp  # This is a method an exported function.
  8. Start the project.

    cd quota-tutorial
    a127 project start
  9. Call the API several times in quick succession. After the third try, as before, the quota policy kicks in and returns a 403 error, as shown below:

    curl http://127.0.0.1:10010/weather?city=Hanover,PA
    
    {"message":"exceeded quota","status":403}

However, note that in this case, the quota is being selectively applied based on the the IP of the incoming request. Requests coming from different client IPs will each be assigned their own quota counter. The console log message from the helper prints out in the terminal where you are running a127.

Interlude

Up until now, we've been running the a127 project locally. This is great for testing. However, at some point you'll want to deploy to a cloud platform. A127 includes direct support for deploying to Apigee Edge and Amazon's Elastic Beanstalk. Let's look at them one at a time.

Deploy to Elastic Beanstalk

Note: To do these steps, you'll need an Amazon Web Services account and access to Elastic Beanstalk. Follow the AWS documentation for details.

Elastic Beanstalk lets you upload and deploy Node.js (and other kinds of) applications to the Amazon Web Services (AWS) cloud. If you have an AWS Elastic Beanstalk account, it's easy to deploy your a127 project there. In this scenario, EB serves as the Node.js container where your a127 project runs.

  1. Create an a127 "account" where you specify AWS as the deployment target. We'll name the account aws-target.

    a127 account create aws-target

    Note: An a127 account is little more than a file with information about a deployment target. It's not an account in the sense that you must sign up or register for something. The account information is stored, by default, in ~/.a127/accounts.

  2. Follow the prompts and specify amazon as the provider:

        a127 account create aws-target
        [?] Provider? amazon
        Account aws-target
        ==================
        provider: amazon
    
        done
  3. Now, be sure to select the account, making it the active account:

    a127 account select aws-target
    
  4. Verify that the account is selected:

    a127 account show
    
    Account
    =======
    provider: amazon
    name: aws-target
    
  5. Run the deploy command (from the root directory):

    a127 project deploy
    

A .zip file is saved in the deployments directory. You can now upload and deploy this zip file to Elastic Beanstalk. See the EB docs for specific import instructions.

For example, if you deploy your project to an EB environment called quotatutorial-env, you could call it like this:

curl -i http://quotatutorial-env.elasticbeanstalk.com/weather?city=Niwot,CO

Deploy to Apigee Edge

Note: You'll need to create an Apigee Edge account before doing these steps. It's free. Just go to the account sign-up page.

Next, we'll show you how to deploy the a127 project to Apigee Edge. Edge serves as the Node.js container where your project server runs. When you deploy on Apigee Edge, you can take advantage of Edge's many API management and analytics features. For more information, see the Apigee documentation.

  1. Create an a127 "account" where you specify Apigee as the deployment target. We'll name the account apigee-target.

    a127 account create apigee-target

    Note: An a127 "account" is specific to a127, and is not related to the account you created on Apigee. An a127 "account" is little more than a file with information about a deployment target. It's not an account in the sense that you must sign up or register for something. The account information is stored, by default, in ~/.a127/accounts.

  2. Follow the prompts and select apigee as the provider. When asked to create a service, say NO. We'll do that later. Use the example below as a guide.

        a127 account create apigee-target
        [?] Provider? apigee
        [?] Do you already have an account on apigee? Yes
        [?] Base URI? https://api.enterprise.apigee.com  
        [?] Organization? jdoe
        [?] User Id? [email protected]   //Use email address
        [?] Password? **************
        [?] Environment? test  //Choose either test or prod. 
        [?] Virtual Hosts? default,secure  //Take the default
        Account apigee-target
        =====================
        baseuri: 'https://api.enterprise.apigee.com'
        organization: jdoe
        username: [email protected]
        password: '******'
        environment: test
        virtualhosts: 'default,secure'
        provider: apigee
    
        [?] Create account service? No   //--We do not need this yet.
        done
  3. Now, be sure to select the account, making it the active account.

    a127 account select apigee-target
    
  4. Verify that the correct account is selected. For example:

    a127 account show
    Account
    =======
    baseuri: 'https://api.enterprise.apigee.com'
    organization: docs
    username: [email protected]
    password: '******'
    environment: test
    virtualhosts: 'default,secure'
    provider: apigee
    name: apigee-target
    
  5. Run the deploy command (from the root directory). This command deploys the project to the Edge account specified in your a127 "account".

    a127 project deploy
    
  6. Bring up the Apigee Edge UI and verify that the project is deployed to whichever environment you chose when you created the a127 "account" (usually this is test or prod).

  7. Test the API. The API path is formed like this:

    http://<your org name>-<your environment name>.apigee.net/<project name>/weather?city=<your city>
    

    For example:

    curl http://jdoe-test.apigee.net/quota-tutorial/weather?city=Niwot,CO
    
  8. As before, call it several times in succession to verify that the quota policy is working. It should return an error after three API calls.

Interlude

Up until now, we've been using the "in memory" quota provider, which stores quota counts in local memory. It's specified in the Swagger file where you declare the quota:

        x-a127-services:
          ## Add the quota policy
          quota:
            provider: "volos-quota-memory"
            options:
              timeUnit: "minute"
              interval: 1
              allow: 3

There's another option, however, which is to use the Apigee quota provider. The Apigee provider stores its quota counts on the Apigee Edge platform. When you use the Edge provider, you get a true distributed quota policy, where counts are maintained correctly across an enterprise server cluster.

It's a good practice to use the Apigee provider if you intend to deploy your API to Apigee; however, it's not necessary to deploy to Apigee to use the Apigee quota provider! You can use the Apigee provider wherever you deploy to. All you have to do is create a RemoteProxy "service" that allows your API to talk to Edge remotely. We'll come to that in a bit and walk you through the steps.

Use the Apigee quota provider

To use the Apigee quota provider, all you need to do is add a line to the x-a127-services declaration for the quota policy:

  1. Open the project in Swagger editor.

    a127 project edit
  2. In the x-a127-services section, replace volos-quota-memory with volos-quota-apigee:

        x-a127-services:
          ## Add the quota policy
          quota:
            #provider: "volos-quota-memory"
            provider: "volos-quota-apigee"
            options:
              timeUnit: "minute"
              interval: 1
              allow: 3
  3. Exit the editor.

  4. Be sure your Apigee account (created previously) is the active account:

    a127 account select apigee-target
    
  5. Run the deploy command (from the project root directory):

    a127 project deploy
    
  6. Optional. Bring up the Apigee Edge UI and verify that the project is deployed to whichever environment you chose (usually this is test or prod). Look under the main menu API > API Proxies.

  7. Upon success, you'll see output like this:

    Deployed:
      name: quota-tutorial
      environment: test
      revision: 5
      state: deployed
      basePath: /
      uris:
        - 'http://docs-test.apigee.net/quota-tutorial'
        - 'https://docs-test.apigee.net/quota-tutorial'
    
    Adding resources...
      GET /weather
    done
    
  8. Test the API. Use the URL for the API deployed on Edge. The correct URL looks like this:

    curl http://<your-org>-<your-env>.apigee.net/<project-name>/weather?city=<your-city>
    

    For example:

    curl http://jdoe-test.apigee.net/quota-tutorial/weather?city=Niwot,CO
    
  9. Call the API several times in succession to verify that the quota policy is working. It should fail after three tries with the error "exceeded quota".

What's different? The deployed project works the same as it did when the "in memory" quota provider was used. However, now we're using the quota service that's built into Apigee Edge. The Edge quota service is more enterprise-grade and robust than the in-memory quota service, offering a distributed quota count across clustered servers. It's recommended to use the Apigee quota provider when you plan to deploy to Edge and for production use cases.

If I don't want to deploy to Edge, can I still use the Apigee Quota service?

You can do this! You need to configure your a127 project so that it can communicate with the Apigee Edge quota as a "remote service". This way, whether you are running locally or deploying to a non-Edge platform (like Elastic Beanstalk), your a127 project can still use the Apigee Edge quota service.

Note: As used here, the term "service" refers to an API proxy that is deployed on Apigee Edge and through which your a127 project communicates with Edge. This "service" is only required if you do not intend to deploy your a127 project to Edge.

Note: You can configure other a127 policies in the same manner. They all follow the same basic pattern described in this example. The only exception is the cache policy. With cache, the only option is an "in-memory" cache.

  1. First, create an a127 RemoteProxy "service". Simply give the service a name and select the default RemoteProxy service type:

        a127 service create my-remote-service
    
        [?] Service Type? (Use arrow keys)
        ❯ RemoteProxy 
        Creating service my-remote-service from apigee-target...
        Remember to bind your service to any projects that require it.
        Service
        =======
        metadata:
          account: apigee-target
          type: RemoteProxy
        data:
          uri: 'http://jdoe-test.apigee.net/my-remote-service'
          key: nqV6FAajIr3b5O2GvEoTzsj1NDcr
    

    Note: The command takes a few moments to complete because it is actually deploying a remote proxy to Apigee Edge. You can look on Apigee Edge to see that this proxy was created and deployed. We'll discuss the uri and key attributes later.

  2. Bind the new remote service to your a127 project.

        cd quota-tutorial
        a127 project bind my-remote-proxy
        Service my-remote-service bound to Project quota-tutorial.
        If this project is deployed, you will need to redeploy to activate the new configuration.
    

Note: We don't need to redeploy because we're going to run the project locally.

  1. Now, we need to tell the quota policy in the Swagger file where to find this remote proxy service. Open the Swagger Editor:

    a127 project edit
    
  2. In the x-a127-config section of the Swagger file, add these key and uri attributes. Replace with the name of the remote proxy service you created previously.

x-a127-config:
    <RemoteProxyName>.key: &apigeeProxyKey CONFIGURED
    <RemoteProxyName>.uri: &apigeeProxyUri CONFIGURED
  1. Next we'll add references to the configured key and uri attributes to the policy declaration, like this:

    Note: We're simply using the YAML syntax for variable substitution. The & symbol is used to assign a value to a variable, and the * symbol is used to reference that variable elsewhere in the YAML file. Now you can see why we bind the remote "service" to the project. It's so the project can retrieve these configured values from the "service" configuration file at runtime.

        x-a127-services:
          ## Add the quota policy
          quota:
            provider: "volos-quota-memory"
            options:
              key: *apigeeProxyKey
              uri: *apigeeProxyUri
              timeUnit: "minute"
              interval: 1
              allow: 3
  2. Start the project:

    a127 project start
    
  3. Call the API four times in quick succession. In the fourth try, you'll get the "exceeded quota" error.

    curl -i http://127.0.0.1:10010/weather?city=Niwot,CO
    {"message":"exceeded quota","status":403}
    

Your locally-running a127 API is using the Apigee Edge quota service. If you want to deploy your project to Elastic Beanstalk or another (not Apigee Edge) Node.js platform, the Edge quota service will continue to work as long as your app can reach Apigee Edge from that deployment.

Working with policies programmatically

As you've seen throughout this tutorial, you can configure and apply policies (like quota) in the Swagger file. However, it's also possible to access and set policies programmatically in a controller file.

Clone this wiki locally