-
Notifications
You must be signed in to change notification settings - Fork 24
tutorial quota
This tutorial walks you through the basic development pattern for an a127 API project. It takes around 30-40 minutes to complete. When you finish, you'll understand:
- How to create a new a127 project
- How an a127 project is structured
- How to use the Swagger Editor
- How to validate a project
- 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 create an a127 "account"
- 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.
We recommend that you:
-
Complete the Hello, world! 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
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.
-
In a new or temp directory, create an a127 project called
quota-tutorial
:a127 project create quota-tutorial
-
CD to the new project directory:
cd quota-tutorial
-
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": "" ...
-
In the project root directory, run
npm install
to pick up the module. -
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. -
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 calledcity
.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
andoperationId
. 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.
-
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.
-
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
-
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 inx-swagger-router-controller
element in the Swagger file. -
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 theoperationId
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.
-
Save the file.
-
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).
-
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.
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.
-
From the project root folder, open the Swagger editor again (if you previously closed it):
a127 project edit
-
Add quota policy block under
x-a127-services
, as shown below. In this context, the term "services" and "policies" are equivalent. You declare policies in thex-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.
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.
-
Now that quota is declared, we're going to apply the policy to the
/weather
path with thex-a127-apply
element. Place the element just belowx-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 ***
-
Open the file
./config/default.yaml
in a text editor. -
Change the
validateResponse
attribute tofalse
and save the file.validateResponse: false
Note: We'll cover response validation in another topic. For now, it's not necessary to enable it.
-
Start the project.
cd quota-tutorial a127 project start
-
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.
Helper functions are called before controllers, and are usually used to set policy values dynamically.
-
CD to your project's root folder.
-
Using a text editor, create a file called
./api/helpers/quota.js
. -
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; }
-
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": "", ...
-
Execute:
npm install
to pick up the module. -
From the project root folder, open the Swagger editor again.
a127 project edit
-
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 thequota.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 asquota
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.
-
Start the project.
cd quota-tutorial a127 project start
-
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.
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.
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.
-
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
. -
Follow the prompts and specify
amazon
as the provider:a127 account create aws-target [?] Provider? amazon Account aws-target ================== provider: amazon done
-
Now, be sure to select the account, making it the active account:
a127 account select aws-target
-
Verify that the account is selected:
a127 account show Account ======= provider: amazon name: aws-target
-
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
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.
-
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
. -
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
-
Now, be sure to select the account, making it the active account.
a127 account select apigee-target
-
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
-
Run the deploy command (from the root directory). This command deploys the project to the Edge account specified in your a127 "account". Apigee recommends that you use the
-u
option to ensure all required packages are uploaded.a127 project deploy -u
-
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).
-
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
-
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.
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.
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:
-
Open the project in Swagger editor.
a127 project edit
-
In the
x-a127-services
section, replacevolos-quota-memory
withvolos-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
-
Exit the editor.
-
Be sure your Apigee account (created previously) is the active account:
a127 account select apigee-target
-
Run the deploy command (from the project root directory):
a127 project deploy -u
-
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.
-
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
-
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
-
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.
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 want to use the quota service provided by Apigee Edge, but 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.
-
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
andkey
attributes later. -
Important: Bind the new remote service to your a127 project.
cd quota-tutorial a127 project bind my-remote-service 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.
-
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
-
In the
x-a127-config
section of the Swagger file, add these key and uri attributes. Replace RemoteProxyName with the name of the remote proxy service you created previously.x-a127-config: <RemoteProxyName>.key: &apigeeProxyKey CONFIGURED <RemoteProxyName>.uri: &apigeeProxyUri CONFIGURED
For example:
x-a127-config: my-remote-service.key: &apigeeProxyKey CONFIGURED my-remote-service.uri: &apigeeProxyUri CONFIGURED
-
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-apigee" options: key: *apigeeProxyKey uri: *apigeeProxyUri timeUnit: "minute" interval: 1 allow: 3
-
Start the project:
a127 project start
-
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.
Congratulations! You've completed the basic a127 tutorial.
Here are some things you can try next:
- Try adding a cache policy to your weather API.
- Play around with mock mode.
- Read about the underlying technology for a127 policies like quota, [Volos.js](About Volos.js).
- Check out the a127 samples repo on GitHub.
- Check out the a127 example project on GitHub.
Having Trouble? Try posting your question to the Apigee Community. Or, for more links and resources, check out our Help Page
Need help? Visit the Apigee Community ! |
---|
-
Getting started
-
Add policies to your API
-
Add security policies
-
Deploy your projects
-
Programmatic hooks
-
Good to know about
-
Deep dives
-
Reference topics
-
Troubleshooting and getting help
-
Related resources