Creating an Endpoints API
An Endpoints API is a remote procedure call (RPC) service that provides remote methods accessible to external clients. Each Endpoints API consists of an RPC service class that subclasses the ProtoRPC remote.Service class, and one or more methods. When you define a method, you must also define message classes for the requests coming into that method and the responses returned by it. A message class performs a mapping function so the incoming data can be extracted and supplied to the service method properly, or supplied properly to the outgoing response.
If a request has path or querystring arguments, you'll use a ResourceContainer class for the mapping, instead of a simple message class.
Finally, you will need to decorate the API service class and class methods, and you will need to define message classes for the requests and responses, as described below:
Create the API
To create an Endpoints API:
-
Add the following required imports:
import endpoints from protorpc import messages from protorpc import message_types from protorpc import remote -
Define a subclass of
remote.Serviceand decorate it with@endpoints.apias follows:@endpoints.api(name='yourApi',version='v1', description='Tic Tac Toe API') class TicTacToeApi(remote.Service): ...Notice that your API name and the name of your service class do not need to be the same.
-
Determine what data your method expects from the request and what data will be returned, and create a Message class for the request and response.
class YourResponseMessageClass(messages.Message): message = messages.StringField(1) class YourRequestMessageClass(messages.Message): message = messages.StringField(1)Note that if no arguments will appear in the request body, such as in a GET request, you could omit the message class for the request and simply use the value
message_types.VoidMessage.If your request has path or querystring arguments, replace
YourRequestMessageClasswith an appropriate ResourceContainer.For complete information on forming and using message classes, see the documentation for the Google Protocol RPC response and request message classes.
-
Create the desired method for your API, and decorate it with
@endpoints.methodas follows:@endpoints.method(YourRequestMessageClass, YourResponseMessageClass, name='foo.bar', ...) def bar(self, request): ...If your request has path or querystring data, replace
YourRequestMessageClasswith an appropriate ResourceContainer.Each of the decorators (
@endpoints.apiand@endpoints.method) is described in more detail below. -
Add the API server code, as described in Creating an API Server.
Defining the API (@endpoints.api)
You can supply several arguments to @endpoints.api to define
your API. The following table describes the available arguments:
| @endpoints.api Arguments | Description | Example |
|---|---|---|
allowed_client_ids |
Required if your API uses authentication. List of client IDs for clients allowed to request tokens. For more information, see Allowed Client IDs and Audiences. | ['1-web-apps.apps.googleusercontent.com','2-android-apps.apps.googleusercontent.com', endpoints.API_EXPLORER_CLIENT_ID] |
audiences |
Required if your API requires authentication and if you are supporting Android clients. A list of client IDs on behalf of which tokens are requested. For more information, see Allowed Client IDs and Audiences. | ['1-web-apps.apps.googleusercontent.com'] |
canonical_name |
Optional. Used to specify a different or more readable name for the API in the client library. This name is used to generate names in the client library; the backend API continues to use the value specified in the name property.For example, if your API has the name set to dfaanalytics, you could use this property to specifiy a canonical name of DFA Group Analytics; the generated client classes would then contain the name DfaGroupAnalytics.You should include the relevant spaces between the names as shown above; these will be replaced by the appropriate camel casing or underscores. |
canonical_name='DFA Analytics' |
description |
A short description of the API. This is exposed in the discovery service to describe your API, and may optionally also be used to generate documentation as described in Generating Client Libraries. | 'Sample API for a simple game' |
documentation |
Optional. The URL where users can find documentation about this version of the API. This will be surfaced in the API Explorer "Learn More" highlight at the top of the API Explorer page and also in the GPE plugin to allow users to learn about your service. | http://link_to/docs |
hostname |
Optional. The host name of your app engine application. | 'your_app_id.appspot.com' |
name |
Required. The name of the API, which is used as the prefix for all of the API's methods and paths. The name value:
|
'yourApi' |
version |
Required. Specifies your Endpoint’s version. | 'v1' |
owner_domain |
Optional. The domain name of the entity that owns the API. Used together with owner_name to provides hints to properly name the client library when it is generated for this API. (The package path will be the reverse of the owner_domain and package_path if supplied. The default is to use appid.apppost.com |
owner_domain=your-company.com |
owner_name |
Optional. The name of the entity that owns the API. Used together with owner_domain to provides hints to properly name the client library when it is generated for this API. |
owner_name=Your-Company |
package_path |
Optional. Is used to further scope The "package" this API belongs to, with values separated by / specifying logical groupings of APIs.For example, specifying cloud/platform will result in the client library path set to cloud/platform/<ApiName> and client library package set to cloud.plaform.<ApiName>. |
package_path=cloud/platform |
scopes |
If not supplied, the default is the email scope (https://www.googleapis.com/auth/userinfo.email), which is required for OAuth. You can override this to specify more [OAuth 2.0 scopes][5] if you wish. You can also override the scopes specified here for a particular API method by specifying different scopes in the @endpoints.method decorator. However, if you do define more than one scope, note that the scope check will pass if the token is minted for any of the specified scopes. |
scopes = {'ss0', 'ss1'} |
title |
Optional. The text displayed in API Explorer as the title of your API, and exposed in the discovery and the directory services. | title=My Backend API |
Allowed Client IDs and Audiences
If you want your Endpoints API to authenticate callers, you need to supply
a list of allowed_client_ids that are allowed to request tokens
that can be used with your application. This list should consist of the all
client IDs you have obtained through the Google Cloud Console for your web,
Android,
iOS,
and other client apps. (This means that the clients must be known at API build-time.)
If you specify an empty list, no clients can access the API.
If you use the allowed_client_ids argument and you want to test authenticated
calls to your API using the Google API Explorer, you must supply its client ID
in the list of allowed_client_ids: the value to use is
endpoints.API_EXPLORER_CLIENT_ID.
For Android clients, in addition to allowed_client_ids, you must also
configure audiences for your API. The audiences argument is used
only for Android clients. The audiences argument specifies the list of
client IDs on behalf of which the token is requested.
Defining an API Method (@endpoints.method)
To create a method in your Endpoints API, decorate the corresponding Python
method with @endpoints.method, supplying arguments to configure the
use of the method. For example, you'll specify the
request and response message classes to be used.
The available arguments are listed in the following table:
| @endpoints.method Arguments | Description | Example |
|---|---|---|
| Request Message Class | The Google Protocol RPC request message class to be used in the method call. Alternatively, you can supply the name of the class. | YourRequestClass |
| Response Message Class | The Google Protocol RPC response message class to be used in the method call. Alternatively, you can supply the name of the class. | YourResponseClass |
name |
An alternative name for this method. The name value:
|
'yourApi' |
path |
The URI path to use to access this method. If you don't set this, the empty string is the path. | 'yourapi/path' |
http_method |
The HTTP method to use. If you don't set this, 'POST' is used by default. |
'GET' |
audiences |
Overrides the equivalent argument specified in @endpoints.api. For more information, see Allowed Client IDs and Audiences. |
['1-web-apps.apps.googleusercontent.com'] |
allowed_client_ids |
This setting overrides the equivalent attribute specified in @endpoints.api. For more information, see Allowed Client IDs and Audiences. |
['1-web-apps.apps.googleusercontent.com', '2-android-apps.apps.googleusercontent.com'] |
Using ResourceContainer for path or querystring arguments
If the request contains path or querystring arguments, you cannot use a simple
Message class as described under Create the API. Instead,
you must use a ResourceContainer class, as follows:
-
Define a message class that has all the arguments that will be passed in the request body. (If no arguments will appear in the request body, you don't need to define a message class: simply use
message_types.VoidMessage.) For example:class MyRequest(messages.Message): my_string = messages.StringField(1) my_number = messages.IntegerField(2, required=True) -
Define a
ResourceContainerwith the above message class as its first parameter, and subsequent parameters for the path and querystring arguments. For example:YOUR_RESOURCE_CONTAINER = endpoints.ResourceContainer( MyRequest, times=messages.IntegerField(2, variant=messages.Variant.INT32, required=True))where
MyRequestis the message class for the data in the request body andtimesis a number expected in the path or querystring accompanying the request. -
Supply the
ResourceContainerto the method handling the request, in the first parameter replacing the request message class that would otherwise be supplied in that location:@endpoints.method(YOUR_RESOURCE_CONTAINER, YourResponseMessageClass, path='yourApi/{times}', http_method='GET', name='greetings.getGreeting') def greeting_get(self, request): -
Add the
pathparameter as shown, to include your API. - If your
ResourceContainerhas a required argument, a client request must include it either in a querystring (for example,yourApi?times=2), or the URL path (for example,yourApi/2). However, in order for your API to receive an argument value via the URL path, you must also add the argument name to the API path as shown above for the{times}argument inpath='yourApi/{times}.
Creating an API Implemented with Multiple Classes
If you implement your API using more than one class, you use a slightly different decoration scheme. For example:
an_api = endpoints.api(name='library', version='v1.0')
@an_api.api_class(resource_name='shelves')
class Shelves(remote.Service):
...
@an_api.api_class(resource_name='books', path='books')
class Books(remote.Service):
...
where you replace an_api with any name you want, so long as you use the same
name for each class in the API. You must precede each class in the API with
the decorator as shown in the snippet above.
About the resource name argument
The optional resource name argument for api_class is the name of the class
that you want to exposed in the API; this is the name that will show up in the
API Explorer, prepended to any methods exposed in the class.
About the path argument
You don't have to explicitly specify a path argument, which indicates the
relative location at which the class methods will appear. Thus, in the snippet
above, no path is specified for the class Shelves, so its methods will be accessible under
/_ah/api/library/v1.
If you do specify a path, the path is appended to the root, but prepended to any paths provided within the class. It is the prefix that appears by default before any paths specified in the class.
In the snippet above, notice that the path books is specified for class
Books. This means that any paths specified for its class methods will be
appended to the class, as shown below:
@an_api.class(resource_name='books', path='books')
class Books(remote.Service):
@endpoints.method(Req, Resp, path='bookmark')
def bookmark_func(self, request):
where bookmark_func is accessible via the path
/_ah/api/library/v1/books/bookmark
Serving a Multi-class API
In your endpoints.api_server
code that creates the API server, you supply the name you assigned for your
api_class collection. For example, where the collection name is:
an_api = endpoints.api(name='library', version='v1.0')
you would create the server as follows:
endpoints.api_server([an_api])