[[breaking-changes]] == Breaking changes coming from the old client If you were already using the previous version of this client – the one you used to install with `npm install elasticsearch` – you will encounter some breaking changes. [discrete] === 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. [discrete] === 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 {es} `v6`, you are 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. * There is no longer 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 only 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`. Every error class has been renamed, and now each is suffixed with `Error` at the end. * Removed errors: `RequestTypeError`, `Generic`, and all the status code specific errors (such as `BadRequest` or `NotFound`). * Added errors: `ConfigurationError` (in case of bad configurations) and `ResponseError` that contains all the data you may need to handle the specific error, such as `statusCode`, `headers`, `body`, and `message`. * Renamed errors: ** `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 way to do it, via the `node` parameter. * Certificates are verified by default, if you want to disable certificates verification, you should set the `rejectUnauthorized` option to `false` inside the `ssl` configuration: [source,js] ---- const { Client } = require('@elastic/elasticsearch') const client = new Client({ ssl: { rejectUnauthorized: false } }) ---- * 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 need to 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 no longer accepts the `query` key. Use the `querystring` key instead (which can be a string or an object). You also need to send a bulk-like request instead of the `body` key, use the `bulkBody` key. 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) }) ---- [discrete] === Talk is cheap. Show me the code. You can find a code snippet with the old client below 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) } }) ----