1. Jean-Jacques Dubray
  2. reacall

Source

reacall /

Filename Size Date modified Message
dist
src
9.1 KB
Updated the README file to explain the additional concepts

ReaCall

Introduction

API Client SDKs are a must have for API providers who are looking to grow a vibrant ecosystem of 3rd party application developers. However, they are generally difficult to build and maintain, especially when the API evolves rapidly over time.

You can always push the responsibility of building client-side SDKs to the developers, but that approach creates some friction, taking away precious resources from the developers which need to build plumbing, when they should be building solutions, and often breaking these solutions when the API changes.

Project ReaCall combines React.js and Swagger to make it easier for API providers to develop and evolve API Client SDKs (ACS).

ReaCall is a simple pattern enabled by React.js and Swagger's metadata. A ReaCall component is a simple React.js component which acts as a mediator between an application, its views and some 3rd party API. The application can either be Web (client-side or server-side) or Native, per React.js deployments models.

                ---------------                --------------
                |   ReaCall   |                |            |
  -- event -->  |  Component  | -- render: --> |    View    |
                |    (SDK)    |                |            |
                ---------------                --------------
                       |
                    API call
                 (e.g. jquery)
                       |
                       v

From the developer's perspective, ReaCall components are a black box which purpose is to respond to application events by invoking specific API calls and passing the resulting data elements to application views which will render them.

ReaCall components are designed such that the developers would not have to customize their behavior and they can be replaced when a new version becomes available, without requiring any change in the Web Application or the View (unless, of course, the application needs to implement some new functionality enabled by the new version of the API). This gives a lot of flexibility in a way the API provides can evolve their APIs.

The nature of React.js [1] makes easy to integrate ReaCall with any Web Application Architecture. React.js also supports a "Server-Side-Rendering" mode which allows using ReaCall components on the server (e.g. node.js).

ReaCall components are self-documented using a Swagger compatible format. To that effect, ReaCall comes with a couple of React.js components which produce Swagger like documentation from the components metadata.

Last but not least, React.js makes it very easy for API providers to build forms which produce the events that will result in "ReaCalling" an operation. This means that simple console like functionality can be added to the SDK along with the documentation.

Here is a code sample that produces the API documentation, which can easily be integrated wthin the API provider's Web site.

   /// API Documentation

   <div id="api-descriptor-container">
        component/api-descriptor.js [Overall]
   </div>

   <script type="text/jsx"> 
        React.render(
            <ApiDoc config={config}>
                <Pet info={true}/>
                <Store info={true}/>
                <User info={true}/>
            </ApiDoc>,
            document.getElementById('api-descriptor-container')
        ) ;
    </script>

ReaCall Specification

The goal of this section is to specify the structure of ReaCall components (methods and metadata).

A ReaCall component is expected to expose the following methods:

  • From React.js

    • propTypes:
    • mixins: [configMixIn,…],
    • getDefaultProps:
    • getInitialState:
    • componentDidMount:
    • componentWillReceiveProps:
    • render:
  • From Swagger // from Swagger

    • apiInfo:
    • schemes:
    • host:
    • basePath:
    • resourcePath:
    • operations: //metadata

    • {operation}: //invoke API

From the perspective of the Swagger format, ReaCall maps all paths to individual components, every other piece of metadata is strictly compatible.

    ---------------                
    |   Swagger   |               
    |     File    |  
    |             |
    |  header:    |             --------------
    |    host,    ------------> |apiconfig.js|
    |    version, |             --------------
    |    ...      |
    |  paths:     |             --------------
    |  /path1/{id} -----------> | ReaCall    |
    |             |             | Component  | ----> operations: [ get,.. ]
    |             |             --------------
    |             |
    |             |             --------------
    |  /path2/...  -----------> | Another    |   
    |             |             | ReaCall    |
    |             |             | Component  | ----> operations: [ get,.. ]
    |             |             --------------
    ---------------

A typical ReaCall component will be implemented like this:

var Resource = React.createClass({

    // React.js "schema language"
    propTypes: { 
        // Request
        // e.g.
        id: React.PropTypes.string.isRequired,

        // Response

        },

    // Swagger's header metadata is mounted
    // via a MixIn
    mixins: [configMixIn],

    getDefaultProps: function() {
        return {
        // As needed
        }
    },

    apiInfo: function() {
        return this.config.info ;
    },

    schemes: [
        "http", "https"
    ],

    host: function() {
        return this.schemes[this.props.scheme]+'://'+this.config.host ;
    },

    basePath: function() {
        return this.config.basePath ;
    },

    resourcePath: function(id,params) {
        if (params) {
            qp = '?'+queryParameters(params) ;
        }
        return  '/resources/'+id + qp ;
    },

    //That metadata is copy/paste from Swagger
    operations: [{
        // Typically a ReaCall component will implement a single path
        // as expressed in the resourcePath method
        // the operations array specifies the operations available 
        // on that resource path (get, post, put, delete...)
        get: {
             tags: [
                 "metadata","react"
             ],
             summary: "Find site by ID",
             description: "Returns the information relative to a resource",
             operationId: "getResourceById",
             produces: [
                 "application/xml",
                 "application/json"
             ],
             parameters: [
                 {
                     "name": "resourceId",
                     "in": "path",
                     "description": "ID of resource to return",
                     "required": true,
                     "type": "string",
                     default: "1234"
                 }
             ],
             responses: {
                 200: {
                     description: "successful operation",
                     schema: {
                         ref: "#/definitions/Resource"
                     }
                 },
                 400: {
                     description: "Invalid ID supplied"
                 },
                 404: {
                }
             },
             security: [
                 {
                     api_key: []
                 }
            ]
        }
    }],

    // operations (using Swagger's operationId as the method name
    getResourceById: function(id) {
        // ... (API call code goes here)
    },

    getInitialState: function() {
        return {
        } ;
    },

    componentDidMount: function() {
        // No need to fetch data if the component is
        // in info or display mode
        if ((!this.props.info) || (!this.props.display)) {
            this.getResourceById(this.props.id) ;
        }
    },

    componentWillReceiveProps: function(nextProps) {
        // No need to fetch data if the component is
        // in info or display mode
        if ((!this.props.info) || (!this.props.display)) {
            this.getResourceById(this.props.id) ;
        }
    },

    render: function() {

        // ... (call to the corresponding view component)
    }

}) ;

There are a couple of optional concepts that can facilitate the development of complex applications: 1) API Components which perform an api call and do not instantiate a child view - they are generally "invisible" - use a handler to call back the parent component and share the response

2) Parents of the API Component many need to "pass" properties to the API Component's child view

Who uses ReaCall?

Contribute

We welcome feedback, contributions and enhancements to both the code and the specification.

[1] Sing Li, "React: Create maintainable, high-performance UI components" http://www.ibm.com/developerworks/library/wa-react-intro/index.html

(c) 2015 Jean-Jacques Dubray | VIMOC Technologies, All rights reserved