Code on GitHub Slack Platform Home

Migrating to v5.x

Migration Guide (v4 to v5)

This tutorial will guide you through the process of updating your app from using the @slack/client package (v4.x) to using the new, improved, and independent packages, starting with v5.0.0.

If you were not using any deprecated features, this should only take a couple minutes.

Note: If you were using the @slack/events-api or @slack/interactive-messages packages, this migration doesn’t affect your app. Those packages only moved repositories, but did not get updated in this process. You’re done!

Update to a supported version of Node

These package have dropped support for versions of Node that are no longer supported. We recommend updating to the latest LTS version of Node, which at this time is v10.15.3. The minimum supported version is v8.9.0.

Note: Learn more about our support schedule so that you can prepare and plan for future updates.

Choose the right packages

In v4.x versions, the package came with a few classes. If your app only needed one or two of these classes, then downloading the whole package needlessly increased the size of your dependencies. Your app no longer has to download or import the code it won’t be using.

Identify which classes your app imported from @slack/client. The following table helps you choose and install the right package(s) for your app, depending on which classes the app used.

Class name Command to install Changes
WebClient npm install @slack/web-api WebClient changes
RTMClient npm install @slack/rtm-api RTMClient changes
IncomingWebhook npm install @slack/webhook IncomingWebhook changes

After you’ve installed the right package(s), remove @slack/client with the following command:

$ npm uninstall @slack/client

Next, change all the require() or import statements from using the @slack/client name, to the name of the new package. For example:

// Before:
const { WebClient } = require('@slack/client');

// After:
const { WebClient } = require('@slack/web-api');

Finally, apply the changes for the individual classes used in your app in the sections below.

Shortcut: While we recommend migrating to the individual packages as soon as possible, one way to ease into the process is to update the @slack/client dependency in your app to v5.0.0. This version just imports the @slack/web-api, @slack/rtm-api, and @slack/webhook packages, and re-exports the same classes as the v4.x versions. The breaking changes below would still need to be addressed, but this sames you the time of adjusting all your require() statements. If you were already avoiding deprecated features, your app will likely just run at that point! Once you’ve adjusted for any breaking changes, we still recommend that you follow up by migrating to the individual packages, as they are lighter and will save you time and disk space when you install your dependencies the next time.

WebClient

Callbacks to Promises

The WebClient no longer supports callback functions to receive results and errors of API method calls. We recommend that you migrate to using Promises and async functions instead. Using Promises can simplify your code by reducing nesting, generally referred to as “the pyramid of doom”.

Before:

// Making a Web API call with a callback
web.chat.postMessage({ text: 'Hello', channel: 'C123456' }, (error, result) => {
  // Error handling
  if (error) {
    console.log(error);
    return;
  }

  // Using the result
  console.log(result);

  // Making another Web API call
  web.users.list({}, (error, result) => {
    // More error handling
    if (error) {
      console.log(error);
      return;
    }

    // Using the second result
    console.log(result);
  });
});

After:

// Wrap in an async function
(async () => {
  try {
    // First API call
    const result = await web.chat.postMessage({ text: 'Hello', channel: 'C123456' });

    // Use first result
    console.log(result);

    // Second API call
    const result2 = await web.users.list();

    // Use second result
    console.log(result2);
  } catch (error) {
    // Only handle errors once
  }
})();

If you prefer to still use callbacks, Node ships with a util.callbackify(), which can be used to wrap method calls so that there’s no need to deal with Promises.

After:

const { callbackify } = require('util');

// Wrap Promise returning function to make a callback accepting function
const chatPostMessage = callbackify(web.chat.postMessage);

chatPostMessage({ text: 'Hello', channel: 'C123456' }, (error, result) => {
  // Normal callback with error-first
});

Response metadata

If your app read the scopes, acceptedScopes, or retryAfter values from the result of an API call, those values have been moved, just slightly, to properties of the response_metadata.

Before:

(async () => {
  const result = await web.chat.postMessage({ text: 'Hello', channel: 'C123456' });

  // These values were properties of the result
  console.log(result.scopes);
  console.log(result.acceptedScopes);
  console.log(result.retryAfter);
})();

After:

(async () => {
  const result = await web.chat.postMessage({ text: 'Hello', channel: 'C123456' });

  // Now they are properties of `response_metadata`
  console.log(result.response_metadata.scopes);
  console.log(result.response_metadata.acceptedScopes);
  console.log(result.response_metadata.retryAfter);
})();

Simplified agent option

If your app is using the agent option and its working, its most likely going to continue to work.

Only if your app set the agent option to an object with an http or an https property, you should consolidate the value by only using the value of the https property. This is a simplified design, because only the https value would have been used when both were defined anyway.

Before:

const web = new WebClient(token, {
  // An agent with both the `http` and `https` property defined
  agent: {
    http: proxyAgent,
    https: proxyAgent,
  },
});

After:

const web = new WebClient(token, {
  // Consolidated into one value
  agent: proxyAgent,
});

Removed methods

The files.comments.add and files.comments.edit named methods were removed. If you still need to use them, you can use the .apiCall(methodName, options) method instead, but we recommend that you instead use threaded messages instead of file comments.

All named methods in the apps.* family of methods were removed. Again, you can use the the .apiCall(methodName, options) method instead, but we recommend migrating your app to using bot tokens instead of building workspace apps.

Token rotation

Workspace apps allowed for short-lived tokens that the WebClient could automatically refresh. This required initializing the WebClient with a clientId, clientSecret, and refreshToken. Since workspace apps are no longer supported, this functionality has been removed. We recommend migrating your app to using bot tokens instead of building workspace apps.

Error code changes

The string values of error codes in ErrorCode export have changed. If your app compares the error.code with a string literal, you need to update that code. Instead, compare with a property of the export such as ErrorCode.RequestError.

If your app used the ErrorCode.WebAPICallReadError export to compare with error.code, you can remove this comparison. The WebAPICallReadError was never used by the WebClient.

Logger objects

If your app set the logger option to a function, you need to update the code to instead use an object with methods for each log level. See details in the logging documentation.

New retry policies

If your app used the retryPolicies export from @slack/client, you need to adjust your code. The values have been renamed.

RTMClient

Callbacks to Promises

The RTMClient no longer supports callback functions to receive results and errors of .sendMessage(). We recommend that you migrate to using Promises and async functions instead. Using Promises can simplify your code by reducing nesting, generally referred to as “the pyramid of doom”.

Before:

// Sending a message using a callback
rtm.sendMessage('Hello', 'C123456', (error, reply) => {
  // Handle error
  if (error) {
    console.log(error)
    return;
  }

  // Use result
  console.log(reply);
});

After:

(async () => {
  try {
    const reply = await rtm.sendMessage('Hello', 'C123456');

    // Use result
    console.log(reply);
  } catch (error) {
    // Handle error
    console.log(error);
  }
})();

Raw messages

If your app was listening for the raw_message event, you should update the code to instead use the slack_event event. The raw_message event emitted a string, which was encoded in JSON, so you typically needed to parse it. The slack_event event emits an object, which is already parsed, so you can skip calling JSON.parse(event).

Simplified agent option

If your app is using the agent option and its working, its most likely going to continue to work.

Only if your app set the agent option to an object with an http or an https property, you should consolidate the value by only using the value of the https property. This is a simplified design, because only the https value would have been used when both were defined anyway.

Before:

const rtm = new RTMClient(token, {
  // An agent with both the `http` and `https` property defined
  agent: {
    http: proxyAgent,
    https: proxyAgent,
  },
});

After:

const rtm = new RTMClient(token, {
  // Consolidated into one value
  agent: proxyAgent,
});

Error code changes

The RTM prefix from the property names of ErrorCode have been removed. For example, ErrorCode.RTMWebsocketError is now ErrorCode.WebsocketError.

The string values of error codes in ErrorCode export have changed. If your app compares the error.code with a string literal, you need to update that code. Instead, compare with a property of the export such as ErrorCode.WebsocketError.

Logger objects

If your app set the logger option to a function, you need to update the code to instead use an object with methods for each log level. See details in the logging documentation.

IncomingWebhook

Callbacks to Promises

The IncomingWebhook no longer supports callback functions to receive results and errors of .send(). We recommend that you migrate to using Promises and async functions instead. Using Promises can simplify your code by reducing nesting, generally referred to as “the pyramid of doom”.

Before:

// Sending a message using a callback
webhook.send('Hello', (error, reply) => {
  // Handle error
  if (error) {
    console.log(error)
    return;
  }

  // Use result
  console.log(reply);
});

After:

(async () => {
  try {
    const reply = await webhook.send('Hello');

    // Use result
    console.log(reply);
  } catch (error) {
    // Handle error
    console.log(error);
  }
})();

Simplified agent option

If your app is using the agent option and its working, its most likely going to continue to work.

Only if your app set the agent option to an object with an http or an https property, you should consolidate the value by only using the value of the https property. This is a simplified design, because only the https value would have been used when both were defined anyway.

Before:

const webhook = new IncomingWebhook(token, {
  // An agent with both the `http` and `https` property defined
  agent: {
    http: proxyAgent,
    https: proxyAgent,
  },
});

After:

const webhook = new IncomingWebhook(token, {
  // Consolidated into one value
  agent: proxyAgent,
});

Error code changes

The IncomingWebhook prefix from the property names of ErrorCode have been removed. For example, ErrorCode.IncomingWebhookRequestError is now ErrorCode.RequestError.

The string values of error codes in ErrorCode export have changed. If your app compares the error.code with a string literal, you need to update that code. Instead, compare with a property of the export such as ErrorCode.RequestError.