Jailene joined Livongo as a front end engineering intern during the summer of 2019. She is a rising senior at Stanford University majoring in Computer Science. This post is about Jailene’s summer project, which was to remove the front end middleware and have the web UI access platform APIs through Livongo’s proxy server.
THE PROBLEM
Web UI ↔ MW ↔ Platform APIs
Livongo originally envisioned using middleware as a “backend for the front end” to provide customized APIs for the needs of the front end. Middleware provided single APIs for fetching all the data needed for a particular page instead of the web UI having to make multiple calls to the platform APIs. Middleware also helped in providing a frontend-specific variant of a platform API such as filtering out unneeded data fields in the response payload for efficiency. We did use middleware for these benefits for some time, but eventually we found it unnecessary as the front end pages themselves were componentized and could make per-component calls to the platform APIs as needed.
We then tried using GraphQL instead of middleware by providing GraphQL endpoints in the platform APIs. Unfortunately, our mobile developers still needed individual platform APIs to be developed because there were challenges using the available mobile client libraries for GraphQL. Given that and our team’s experience and comfort level using platform APIs’ traditional REST APIs, GraphQL was largely abandoned. We were left with the burden and costs of using middleware: extra latency on every API call, extra complexity in the system, and the development cost of maintaining it.
THE CHANGE
Web UI ↔ Proxy Server ↔ Platform APIs
Livongo’s web UI now bypasses the middleware layer and uses a simple nginx proxy server to access the platform APIs; the proxy layer is necessary because of security purposes such as restricting what platform APIs are accessible to the front end. With this modification, development time has been reduced and the web UI’s performance has improved because APIs complete at a faster rate.
Transitioning APIs to use the proxy URL has consisted of 3 main phases. The first phase involved finding every API in each web flow and testing each, one by one, to see if it successfully completed with the proxy URL. If it did, then the API would have a useProxy parameter that would trigger the HTTP method to use the proxy URL. In the second phase, the proxy URL was made the default, so the useProxy parameter was removed and the few APIs that still needed to use middleware have a useMW parameter.
The proxy URL was added to the config files:
config.api.backend.proxyURL = "https://backend-api.livongo.com"
The code below shows an API that still needs to go through middleware:
return this.get('/v1/coach/appointment/types', params,
{'Content-Type': 'application/json'}, {useMW: true});
In each HTTP method (get, post, and delete), the URL is determined by the useMW parameter. If the API has this parameter, then it uses middleware and if it doesn’t, then it uses the proxy URL:
config = _.extend({}, config, { method: method, url: _.isUndefined(options.useMW) ? getFullProxyURL(url) : getFullURL(url), timeout: 30000 // 30 seconds });
The final phase aims to completely remove the front end middleware, so eventually there won’t be a useMW parameter and the URL for an HTTP method will default to be the proxy URL. As of right now, only about 90% of APIs go through the proxy server and the remaining 10% of APIs still go through middleware. Services such as Acuity and Webinato are not supported by our backend yet, so they rely on middleware. APIs that use a system token, such as an encryption API, cannot be exposed to the front end yet because it’s not secure, so they still have to go through middleware. We are currently working on transitioning these APIs to go through the proxy server so that we can completely become independent of the front end middleware.
Challenges
Some APIs required more modifications to successfully complete when going through the proxy server. A few APIs, such as the API to authenticate a login to member portal (/v1/users/me/auth), were blocked by CORS policy, so a Site Reliability Engineer added rules to our web proxy layer to add CORS headers for the hosts that needed them.
Results
The table below compares the average speeds of APIs called 5 times each with middleware and with the proxy URL. As an example, when a Livongo member enters their member portal and navigates to their logbook, the API called to access the data on their bgs completes 61% faster when it uses the proxy URL instead of going through middleware. Not having to maintain middleware has also reduced the amount of development work for the Front End team which has enabled them to be more agile.
API (5 times) | MW
(ms) |
proxy
(ms) |
net
(ms) |
% improvement |
/v1/users/me/auth | 654 | 445 | -209 | 32 |
/v1/users/v2/recruitable | 753 | 544 | -209 | 28 |
/v1/accounts/me/info | 334 | 165 | -169 | 51 |
v1/users/me/readings/bgs/data | 292 | 113 | -179 | 61 |
/v1/users/eligible/matching | 373 | 222 | -151 | 40 |
/v1/users/me/shares | 278 | 133 | -145 | 52 |
Our recruitable and member populations are growing rapidly, so we are striving to enhance the efficiency of our web UI to give them a quick and smooth flow experience when they are registering, accessing their member portal, setting up a coaching session, or going through any other web flow.
Acknowledgements
Thank you for all the help and support from Max Vuong, Ray Thro, and the Front End team in making this project possible!