14-day free trial
Create a full-featured search experience in no time.
Sorry, there is no results for this query
Algolia’s DNA is really about performance. We want our search engine to answer relevant results as fast as possible.
The backend and frontend share the same code.
Here are the main features of this new API client:
Before being able to merge the Node.js and browser module, we had to remember how the current code is working. An easy way to understand what a code is doing is to read the tests. Unfortunately, in the previous version of the library, we had only one test. One test was not enough to rewrite our library. Let’s go testing!
When no tests are written on a library of ~1500+ LOC, what are the tests you should write first?
Unit testing would be too close to the implementation. As we are going to rewrite a lot of code later on, we better not go too far on this road right now.
From a testing point of view, this can be summarized as:
Indeed, having to reach Algolia servers in each test would introduce a shared testing state amongst developers and continuous integration. It would also have a slow TDD feedback because of heavy network usage.
This is not unit testing nor integration testing, but in between. We also planned in the coming weeks on doing a separate full integration testing suite that will go from the browser to our servers.
Two serious candidates showed up to help in testing HTTP request based libraries
This seems complex but we really want to be available and compatible with every browser environment.
As a result, we created algolia/faux-jax. It is now pretty stable and can mock XMLHttpRequest, XDomainRequest and even http module from Node.js. It means faux-jax is an isomorphic HTTP mock testing tool. It was not designed to be isomorphic. It was easy to add the Node.js support thanks to moll/node-mitm.
The testing stack is composed of:
The fun part is done, now onto the tedious one: writing tests.
We divided our tests in two categories:
Simple test cases were written as table driven tests:
Creating a testing stack that understands theses test-cases was some work. But the reward was worth it: the TDD feedback loop is great. Adding a new feature is easy: fire editor, add test, implement annnnnd done.
Complex test cases like JSONP fallback, timeouts and errors, were handled in separate, more advanced tests:
To be able to run our tests we chose defunctzombie/zuul.
For local development, we have an npm test task that will:
You can see the task in the package.json. Once run it looks like this:
But phantomjs is no real browser so it should not be the only answer to “Is my module working in browsers?”. To solve this, we have an npm run dev task that will expose our tests in a simple web server accessible by any browser:
Finally, if you have virtual machines, you can test in any browser you want, all locally:
What comes next after setting up a good local development workflow? Continuous integration setup!
defunctzombie/zuul supports running tests using Saucelabs browsers. Saucelabs provides browsers as a service (manual testing or Selenium automation). It also has a nice OSS plan called Opensauce. We patched our .zuul.yml configuration file to specify what browsers we want to test. You can find all the details in zuul’s wiki.
Right now tests are taking a bit too long so we will soon split them between desktop and mobile.
Once we had a usable testing stack, we started our rewrite, the V3 milestone on Github.
We dropped the new AlgoliaSearch() usage in favor of just algoliasearch(). It allows us to hide implementation details to our API users.
new AlgoliaSearch(applicationID, apiKey, opts);
algoliasearch(applicationID, apiKey, opts);
client.method(param, callback, param, param);
client.method(params, param, param, params, callback);
This allows our callback lovers to use libraries like caolan/async very easily.
Promises are a great way to handle the asynchronous flow of your application.
— pouchdb (@pouchdb) March 10, 2015
We implemented both promises and callbacks, it was nearly a no-brainer. In every command, if you do not provide a callback, you get a Promise. We use native promises in compatible environments and jakearchibald/es6-promise as a polyfill.
The main library was also previously exporting window.AlgoliaSearchHelper to ease the development of awesome search UIs. We externalized this project and it now has now has a new home at algolia/algoliasearch-helper-js.
The previous version was directly exporting multiple properties in the window object. As we wanted our new library to be easily compatible with a broad range of module loaders, we made it UMD compatible. It means our library can be used:
This was achieved by writing our code in a CommonJS style and then use the standalone build feature of browserify.
Every build then need to define how to:
Using a simple inheritance pattern we were able to solve a great challenge.
Finally, we have a build script that will generate all the needed files for each environment.
But we also wanted to provide a good experience for our previous users when they wanted to upgrade:
Having separated the builds implementation helped us a lot, because the Node.js build is a regular build only using the http module from Node.js.
Then we only had to tell module loaders to load index.js on the server and src/browser/.. in browsers.
This last step was done by configuring browserify in our package.json:
If you are using the algoliasearch module with browserify or webpack, you will get our browser implementation automatically.
The faux-jax library is released under MIT like all our open source projects. Any feedback or improvement idea are welcome, we are dedicated to make our JS client your best friend 🙂