[[breaking-changes]] == Breaking changes coming from the old client If you were already using the previous version of this client --i.e. the one you used to install with `npm install elasticsearch`-- you will encounter some breaking changes. === Don’t panic! Every breaking change was carefully weighed, and each is justified. Furthermore, the new codebase has been rewritten with modern JavaScript and has been carefully designed to be easy to maintain. === Breaking changes * Minimum supported version of Node.js is `v8`. * Everything has been rewritten using ES6 classes to help users extend the defaults more easily. * There is no longer an integrated logger. The client now is an event emitter that emits the following events: `request`, `response`, and `error`. * The code is no longer shipped with all the versions of the API, but only that of the package’s major version, This means that if you are using Elasticsearch `v6`, you will be required to install `@elastic/elasticsearch@6`, and so on. * The internals are completely different, so if you used to tweak them a lot, you will need to refactor your code. The public API should be almost the same. * No more browser support, for that will be distributed via another module, `@elastic/elasticsearch-browser`. This module is intended for Node.js only. * The returned value of an API call will no longer be the `body`, `statusCode`, and `headers` for callbacks and just the `body` for promises. The new returned value will be a unique object containing the `body`, `statusCode`, `headers`, `warnings`, and `meta`, for both callback and promises. [source,js] ---- // before const body = await client.search({ index: 'my-index', body: { foo: 'bar' } }) client.search({ index: 'my-index', body: { foo: 'bar' } }, (err, body, statusCode, headers) => { if (err) console.log(err) }) // after const { body, statusCode, headers, warnings } = await client.search({ index: 'my-index', body: { foo: 'bar' } }) client.search({ index: 'my-index', body: { foo: 'bar' } }, (err, { body, statusCode, headers, warnings }) => { if (err) console.log(err) }) ---- * Errors: there is no longer a custom error class for every HTTP status code (such as `BadRequest` or `NotFound`). There is instead a single `ResponseError`. Each error class has been renamed, and now each is suffixed with `Error` at the end. * Errors that have been removed: `RequestTypeError`, `Generic`, and all the status code specific errors (such as `BadRequest` or `NotFound`). * Errors that have been added: `ConfigurationError` (in case of bad configurations) and `ResponseError`, which contains all the data you may need to handle the specific error, such as `statusCode`, `headers`, `body`, and `message`. * Errors that has been renamed: ** `RequestTimeout` (408 statusCode) => `TimeoutError` ** `ConnectionFault` => `ConnectionError` ** `NoConnections` => `NoLivingConnectionsError` ** `Serialization` => `SerializationError` ** `Serialization` => `DeserializationError` * You must specify the port number in the configuration. In the previous version you can specify the host and port in a variety of ways, with the new client there is only one via the `node` parameter. * The `plugins` option has been removed, if you want to extend the client now you should use the `client.extend` API. [source,js] ---- // before const { Client } = require('elasticsearch') const client = new Client({ plugins: [...] }) // after const { Client } = require('@elastic/elasticsearch') const client = new Client({ ... }) client.extend(...) ---- * There is a clear distinction between the API related parameters and the client related configurations, the parameters `ignore`, `headers`, `requestTimeout` and `maxRetries` are no longer part of the API object, and you should specify them in a second option object. [source,js] ---- // before const body = await client.search({ index: 'my-index', body: { foo: 'bar' }, ignore: [404] }) client.search({ index: 'my-index', body: { foo: 'bar' }, ignore: [404] }, (err, body, statusCode, headers) => { if (err) console.log(err) }) // after const { body, statusCode, headers, warnings } = await client.search({ index: 'my-index', body: { foo: 'bar' } }, { ignore: [404] }) client.search({ index: 'my-index', body: { foo: 'bar' } }, { ignore: [404] }, (err, { body, statusCode, headers, warnings }) => { if (err) console.log(err) }) ---- * The `transport.request` method will no longer accept the `query` key, but the `querystring` key instead (which can be a string or an object), furthermore, you need to send a bulk-like request, instead of the `body` key, you should use the `bulkBody` key. Also in this method, the client specific parameters should be passed as a second object. [source,js] ---- // before const body = await client.transport.request({ method: 'GET', path: '/my-index/_search', body: { foo: 'bar' }, query: { bar: 'baz' } ignore: [404] }) client.transport.request({ method: 'GET', path: '/my-index/_search', body: { foo: 'bar' }, query: { bar: 'baz' } ignore: [404] }, (err, body, statusCode, headers) => { if (err) console.log(err) }) // after const { body, statusCode, headers, warnings } = await client.transport.request({ method: 'GET', path: '/my-index/_search', body: { foo: 'bar' }, querystring: { bar: 'baz' } }, { ignore: [404] }) client.transport.request({ method: 'GET', path: '/my-index/_search', body: { foo: 'bar' }, querystring: { bar: 'baz' } }, { ignore: [404] }, (err, { body, statusCode, headers, warnings }) => { if (err) console.log(err) }) ---- === Talk is cheap. Show me the code. Following you will find a snippet of code with the old client, followed by the same code logic, but with the new client. [source,js] ---- const { Client, errors } = require('elasticsearch') const client = new Client({ host: 'http://localhost:9200', plugins: [utility] }) async function run () { try { const body = await client.search({ index: 'game-of-thrones', body: { query: { match: { quote: 'winter' } } } ignore: [404] }) console.log(body) } catch (err) { if (err instanceof errors.BadRequest) { console.log('Bad request') } else { console.log(err) } } } function utility (Client, config, components) { const ca = components.clientAction.factory Client.prototype.utility = components.clientAction.namespaceFactory() const utility = Client.prototype.utility.prototype utility.index = ca({ params: { refresh: { type: 'enum', options: [ 'true', 'false', 'wait_for', '' ] }, }, urls: [ { fmt: '/<%=index%>/_doc', req: { index: { type: 'string', required: true } } } ], needBody: true, method: 'POST' }) }) ---- And now with the new client. [source,js] ---- const { Client, errors } = require('@elastic/elasticsearch') // NOTE: `host` has been renamed to `node`, // and `plugins` is no longer supported const client = new Client({ node: 'http://localhost:9200' }) async function run () { try { // NOTE: we are using the destructuring assignment const { body } = await client.search({ index: 'game-of-thrones', body: { query: { match: { quote: 'winter' } } } // NOTE: `ignore` now is in a separated object }, { ignore: [404] }) console.log(body) } catch (err) { // NOTE: we are checking the `statusCode` property if (err.statusCode === 400) { console.log('Bad request') } else { console.log(err) } } } // NOTE: we can still extend the client, but with a different API. // This new API is a little bit more verbose, since you must write // your own validations, but it's way more flexible. client.extend('utility.index', ({ makeRequest, ConfigurationError }) => { return function utilityIndex (params, options) { const { body, index, ...querystring } = params if (body == null) throw new ConfigurationError('Missing body') if (index == null) throw new ConfigurationError('Missing index') const requestParams = { method: 'POST', path: `/${index}/_doc`, body: body, querystring } return makeRequest(requestParams, options) } }) ----