title: DRY code for Google Cloud Endpoints APIs
body: <p>I want to avoid boilerplate code for creating Google Cloud Endpoints APIs for different models of my Google App Engine application. Suppose I have a <code>Post</code>, <code>User</code> and <code>Category</code> model. The data is stored in the datastore. I want to create a REST API with the resources <code>posts</code>, <code>users</code> and <code>categories</code>. I have written the following code for the <code>posts</code> resource:</p>
  
  <pre><code>import endpoints
  from protorpc import messages
  from protorpc import message_types
  from protorpc import remote
  from blog.models import Post
  from cloud_endpoints import WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID, ANDROID_AUDIENCE
  
  
  class PostMessage(messages.Message):
      id = messages.StringField(1)
      title = messages.StringField(2)
      body = messages.StringField(3)
  
  
  class PostMessageCollection(messages.Message):
      post_messages = messages.MessageField(PostMessage, 1, repeated=True)
  
  
  def post_to_message(post):
      return PostMessage(
          id=str(post.key()),
          title=post.title,
          body=post.body)
  
  
  ID_RESOURCE = endpoints.ResourceContainer(
      message_types.VoidMessage,
      id=messages.StringField(1, variant=messages.Variant.STRING))
  
  PUT_RESOURCE = endpoints.ResourceContainer(
      PostMessage,
      id=messages.StringField(1, variant=messages.Variant.STRING))
  
  POST_RESOURCE = endpoints.ResourceContainer(Post)
  
  
  @endpoints.api(name='posts',
                 version='v1',
                 allowed_client_ids=[WEB_CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID],
                 audiences=[ANDROID_AUDIENCE])
  class PostsApi(remote.Service):
      """List"""
      @endpoints.method(message_types.VoidMessage,
                        PostMessageCollection,
                        path='/posts',
                        http_method='GET',
                        name='posts.listPosts')
      def list(self, unused_request):
          post_messages = []
          for post in Post.all():
              post_messages.append(post_to_message(post))
  
          return PostCollection(post_messages=post_messages)
  
      """Get"""
      @endpoints.method(ID_RESOURCE,
                        PostMessage,
                        path='/posts/{id}',
                        http_method='GET',
                        name='posts.getPost')
      def get(self, request):
          try:
              return post_to_message(Post.get(request.id))
  
          except (IndexError, TypeError):
              raise endpoints.NotFoundException('Post %s not found.' % (request.id,))
  
      """Create"""
      @endpoints.method(POST_RESOURCE,
                        message_types.VoidMessage,
                        path='/posts',
                        http_method='POST',
                        name='posts.createPost')
      def create(self, request):
          post = Post(title=request.title, body=request.body)\
          post.put()
          return message_types.VoidMessage()
  
      """Update"""
      @endpoints.method(PUT_RESOURCE,
                        message_types.VoidMessage,
                        path='/posts/{id}',
                        http_method='POST',
                        name='posts.updatePost')
      def update(self, request):
          try:
              post = Post.get(request.id)
              post.title = request.title
              post.body = request.body
              return message_types.VoidMessage()
          except (IndexError, TypeError):
              raise endpoints.NotFoundException('Post %s not found.' % (request.id,))
  
      """Delete"""
      @endpoints.method(ID_RESOURCE,
                        message_types.VoidMessage,
                        path='/posts/{id}',
                        http_method='DELETE',
                        name='posts.deletePost')
      def delete(self, request):
          try:
              post = Post.get(request.id)
              post.delete()
              return message_types.VoidMessage()
  
          except (IndexError, TypeError):
              raise endpoints.NotFoundException('Post %s not found.' % (request.id,))
  </code></pre>
  
  <p>I could copy/paste this code and change "Post" to "Category" everywhere, and edit  <code>PostMessage</code>, <code>PostMessageCollection</code> and <code>post_to_message</code>, but that seems bad practise. I would like not to repeat myself. Is it possible to create an abstract API class and make subclasses for <code>PostAPI</code>, <code>CategoryAPI</code> and <code>UserAPI</code>? Or is there a better way to parameterize <code>Post</code>, <code>PostMessage</code>, <code>PostMessageCollection</code>, <code>post_to_message</code> and the path to the resource ("/posts", "/categories" and "/users") so that I don't have to copy/paste the class for every resource? The classes would have the same methods with the same decorators, and I would like not to repeat that for every resource. I use Python 2.7.</p>