Slack
Slack is a messaging app for business that connects people to the information they need. By bringing people together to work as one unified team, Slack transforms the way organizations communicate.
The Slack Wrapper is a WebAssembly (Wasm) foreign data wrapper which allows you to query Slack workspaces, channels, messages, and users directly from your Postgres database.
Available Versions#
| Version | Wasm Package URL | Checksum | Required Wrappers Version |
|---|---|---|---|
| 0.2.0 | https://github.com/supabase/wrappers/releases/download/wasm_slack_fdw_v0.2.0/slack_fdw.wasm | bfb0d22ffea2092c049773302c049162a2a57ddc265da59a83116bf29ed40c3a | >=0.5.0 |
| 0.1.0 | https://github.com/supabase/wrappers/releases/download/wasm_slack_fdw_v0.1.0/slack_fdw.wasm | 5b022b441c0007e31d792ecb1341bfffed1c29cb865eb0c7969989dff0e8fdc3 | >=0.4.0 |
| 0.0.6 | https://github.com/supabase/wrappers/releases/download/wasm_slack_fdw_v0.0.6/slack_fdw.wasm | 349cb556f87a0233e25eb608a77e840531bc87f1acf9916856268bdcdd9973e2 | >=0.4.0 |
Preparation#
Before you can query Slack, you need to enable the Wrappers extension and store your credentials in Postgres.
Enable Wrappers#
Make sure the wrappers extension is installed on your database:
1create extension if not exists wrappers with schema extensions;Enable the Slack Wrapper#
Enable the Wasm foreign data wrapper:
1create foreign data wrapper wasm_wrapper2 handler wasm_fdw_handler3 validator wasm_fdw_validator;Create a Slack API Token#
- Visit the Slack API Apps page
- Click "Create New App" and select "From scratch"
- Name your app and select the workspace to install it
- Navigate to "OAuth & Permissions" in the sidebar
- Under "Scopes", add the following Bot Token Scopes:
channels:history- Read messages in public channelschannels:read- View basic channel informationusers:read- View users in workspaceusers:read.email- View email addressesfiles:read- View files shared in channelsreactions:read- View emoji reactionsteam:read- View information about the workspaceusergroups:read- View user groups and their members
- Install the app to your workspace
- Copy the "Bot User OAuth Token" that starts with
xoxb-
Store credentials (optional)#
By default, Postgres stores FDW credentials inside pg_catalog.pg_foreign_server in plain text. Anyone with access to this table will be able to view these credentials. Wrappers is designed to work with Vault, which provides an additional level of security for storing credentials. We recommend using Vault to store your credentials.
1-- Save your Slack API token in Vault and retrieve the created `key_id`2select vault.create_secret(3 'xoxb-your-slack-token' -- Slack Bot User OAuth Token4 'slack',5 'Slack API token for Wrappers'6);Connecting to Slack#
We need to provide Postgres with the credentials to access Slack and any additional options. We can do this using the create server command:
1create server slack_server2 foreign data wrapper wasm_wrapper3 options (4 fdw_package_name 'supabase:slack-fdw',5 fdw_package_url '{See: "Available Versions"}',6 fdw_package_checksum '{See: "Available Versions"}',7 fdw_package_version '{See: "Available Versions"}', -- eg: 0.1.08 api_token_id '<key_ID>', -- The Key ID from Vault9 workspace 'your-workspace' -- Optional workspace name10 );The full list of server options are below:
fdw_package_*: required. Specify the Wasm package metadata. You can get the available package version list from above.api_token|api_token_idapi_token:Slack Bot User OAuth Token, required if not using Vault.api_token_id: Vault secret key ID storing the Slack token, required if using Vault.
workspace- Slack workspace name, optional.
Create a schema#
We recommend creating a schema to hold all the foreign tables:
1create schema if not exists slack;Entities#
The Slack Wrapper supports data reads from the Slack API.
We can use SQL import foreign schema to import foreign table definitions from Slack.
For example, using below SQL can automatically create foreign tables in the slack schema.
1-- create all the foreign tables2import foreign schema slack from server slack_server into slack;34-- or, create selected tables only5import foreign schema slack6 limit to ("messages", "channels")7 from server slack_server into slack;89-- or, create all foreign tables except selected tables10import foreign schema slack11 except ("messages")12 from server slack_server into slack;Messages#
This represents messages from channels, DMs, and group messages.
Ref: Slack conversations.history API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| messages | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.messages (2 ts text,3 user_id text,4 channel_id text,5 text text,6 thread_ts text,7 reply_count integer8)9 server slack_server10 options (11 resource 'messages'12 );Notes#
- The
tsfield is the message timestamp ID and is used as the primary key - Supports query pushdown for channel filtering
- Requires the
channels:historyscope
Channels#
This represents all channels in the workspace.
Ref: Slack conversations.list API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| channels | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.channels (2 id text,3 name text,4 is_private boolean,5 created timestamp,6 creator text7)8server slack_server9options (10 resource 'channels'11);Notes#
- The
idfield is the channel ID and is used as the primary key - Requires the
channels:readscope
Users#
This represents all users in the workspace.
Ref: Slack users.list API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| users | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.users (2 -- Basic information3 id text,4 name text,5 6 -- Name and profile fields7 real_name text,8 display_name text,9 display_name_normalized text,10 real_name_normalized text,11 12 -- Contact information13 email text,14 phone text,15 skype text,16 17 -- Role information18 is_admin boolean,19 is_owner boolean,20 is_primary_owner boolean,21 is_bot boolean,22 is_app_user boolean,23 is_restricted boolean,24 is_ultra_restricted boolean,25 deleted boolean,26 27 -- Status information28 status_text text,29 status_emoji text,30 status_expiration bigint,31 title text,32 33 -- Team information34 team_id text,35 team text,36 37 -- Time zone information38 tz text,39 tz_label text,40 tz_offset integer,41 locale text,42 43 -- Avatar/image URLs44 image_24 text,45 image_48 text,46 image_72 text,47 image_192 text,48 image_512 text,49 50 -- Miscellaneous51 color text,52 updated bigint53)54server slack_server55options (56 resource 'users'57);Notes#
- The
idfield is the user ID and is used as the primary key - Requires the
users:readscope - Email field requires the
users:read.emailscope - Supports query pushdown for filtering by
name,email, andteam_iddirectly via the Slack API - Supports sorting by
name,real_name, andemail - Supports LIMIT and OFFSET clauses for pagination
User Groups#
This represents user groups (a.k.a. user groups) in the workspace.
Ref: Slack usergroups.list API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| usergroups | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.usergroups (2 id text,3 team_id text,4 name text,5 handle text,6 description text,7 is_external boolean,8 date_create bigint,9 date_update bigint,10 date_delete bigint,11 auto_type text,12 created_by text,13 updated_by text,14 deleted_by text,15 user_count integer,16 channel_count integer17)18server slack_server19options (20 resource 'usergroups'21);2223-- Get all user groups24select * from slack.usergroups;2526-- Get user groups sorted by name27select * from slack.usergroups order by name;Notes#
- The
idfield is the user group ID and is used as the primary key - Requires the
usergroups:readscope - Supports query pushdown for filtering by
team_idandinclude_disabled - Supports sorting by
name,handle,date_create, anddate_update - Supports LIMIT and OFFSET clauses for pagination
User Group Members#
This represents the membership relationship between users and user groups.
Ref: Slack usergroups.users.list API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| usergroup_members | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.usergroup_members (2 usergroup_id text,3 usergroup_name text,4 usergroup_handle text,5 user_id text6)7server slack_server8options (9 resource 'usergroup_members'10);1112-- Get all user group memberships13select * from slack.usergroup_members;Notes#
- The
usergroup_idanduser_idfields form a composite primary key - Requires the
usergroups:readscope - Supports LIMIT and OFFSET clauses for pagination
- To avoid excessive API calls and potential rate limiting, consider using materialized views instead of direct joins
Files#
This represents files shared in the workspace.
Ref: Slack files.list API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| files | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.files (2 id text,3 name text,4 title text,5 mimetype text,6 size bigint,7 url_private text,8 user_id text,9 created timestamp10)11server slack_server12options (13 resource 'files'14);Notes#
- The
idfield is the file ID and is used as the primary key - Requires the
files:readscope - Supports query pushdown for filtering by channel or user
Team Info#
This represents information about the team/workspace.
Ref: Slack team.info API
Operations#
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| team-info | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage#
1create foreign table slack.team_info (2 id text,3 name text,4 domain text,5 email_domain text6)7server slack_server8options (9 resource 'team-info'10);Notes#
- Returns a single row with team information
- No special scopes required beyond authentication
Query Pushdown Support#
This FDW supports the following condition pushdowns:
| Resource | Supported Filters | Sorting | Limit/Offset |
|---|---|---|---|
| messages | channel_id, oldest, latest | No | Yes* |
| users | name, email, team_id | name, real_name, email | Yes |
| usergroups | team_id, include_disabled | name, handle, date_create, date_update | Yes |
| usergroup_members | (no filter support) | No | Yes |
| channels | types (public/private) | No | Yes* |
| files | channel_id, user_id, ts_from, ts_to | No | No |
| team-info | (no filter support) | No | No |
* Pagination is supported through cursor-based pagination from the Slack API
Supported Data Types#
| Postgres Data Type | Slack JSON Type |
|---|---|
| text | string |
| boolean | boolean |
| integer | number |
| bigint | number |
| timestamp | string (Unix timestamp) |
Required Slack API Scopes#
Each entity type in the Slack FDW requires specific API scopes. If you get a missing_scope error, you may need to add additional scopes and reinstall your app to the workspace.
| Entity Type | Required Scopes | Error If Missing |
|---|---|---|
| users | users:read | "The token used is not granted the required scopes" |
| users (emails) | users:read.email | "missing_scope" on email fields |
| usergroups | usergroups:read | "missing_scope" when querying user groups |
| usergroup_members | usergroups:read | "missing_scope" when querying user group members |
| channels | channels:read | "missing_scope" when querying channels |
| messages | channels:history, channels:read | "missing_scope" when querying messages |
| files | files:read | "missing_scope" when querying files |
| team-info | team:read | "missing_scope" when querying team info |
Adding Scopes to an Existing App#
If you need to add scopes to an existing Slack app:
- Go to Your Slack Apps
- Select your app
- Click "OAuth & Permissions" in the sidebar
- Under "Bot Token Scopes", click "Add an OAuth Scope"
- Add any missing scopes from the table above
- Scroll up and click "Reinstall to Workspace"
- Approve the new permissions
- Copy the new Bot User OAuth Token (it may have changed)
- Update your token in your database server configuration
Limitations#
This section describes important limitations and considerations when using this FDW:
- Large result sets may experience slower performance due to pagination requirements
- Rate limits are enforced by the Slack API (Tier 2: 20+ requests/minute)
- Messages older than the workspace retention policy are not accessible
- Private channels require the
groups:historyandgroups:readscopes - Direct messages require the
im:historyandim:readscopes
Troubleshooting#
Error: "Slack API error: missing_scope"#
This error occurs when your Slack token doesn't have all the required OAuth scopes for the resource you're trying to access.
Solution:
- Check the Required Slack API Scopes section above to see which scopes are needed for the entity you're querying
- Follow the steps in the "Adding Scopes to an Existing App" section to add the missing scopes
- Reinstall your app to the workspace
- Update your token in the PostgreSQL server configuration
Error: "channel_id is required for querying messages"#
When querying the messages table, you must include a WHERE channel_id = 'CXXXXXXXX' clause.
Solution:
1-- Incorrect (will error):2SELECT * FROM slack.messages LIMIT 10;34-- Correct:5SELECT * FROM slack.messages WHERE channel_id = 'C01234ABCDE' LIMIT 10;Error: "Rate limit exceeded"#
Slack's API enforces rate limits (Tier 2: ~20 requests/minute).
Solution:
- Add more specific WHERE clauses to reduce the number of API calls
- Use smaller LIMIT values
- Consider materialized views for frequent queries:
1CREATE MATERIALIZED VIEW slack_users AS 2SELECT * FROM slack.users;34-- Query the materialized view instead5SELECT * FROM slack_users;67-- Refresh when needed (less frequently)8REFRESH MATERIALIZED VIEW slack_users;Examples#
Below are some examples on how to use Slack foreign tables.
Basic Example#
This example will create a "foreign table" inside your Postgres database and query its data.
1create foreign table slack.channels (2 id text,3 name text,4 is_private boolean,5 created timestamp,6 creator text7)8server slack_server9options (10 resource 'channels'11);1213-- Query all channels14select * from slack.channels;User Information#
1create foreign table slack.users (2 -- Basic subset of fields3 id text,4 name text,5 real_name text,6 email text,7 is_admin boolean,8 is_bot boolean,9 10 -- Additional useful fields11 display_name text,12 phone text,13 title text,14 status_text text,15 status_emoji text,16 team_id text,17 image_192 text18)19server slack_server20options (21 resource 'users'22);2324-- List all admin users25select * from slack.users where is_admin = true;2627-- Get users ordered by name28select * from slack.users order by name;2930-- Get top 5 non-bot users ordered by real_name 31select * from slack.users where is_bot = false order by real_name limit 5;3233-- Filter users by email domain34select * from slack.users where email like '%@example.com' order by name;3536-- Find user by name (pushes filter down to API)37select * from slack.users where name = 'johndoe';3839-- Show user with their profile picture40select id, name, real_name, image_192 as profile_picture from slack.users 41where name = 'johndoe';User Groups Example#
1-- Create tables2create foreign table slack.usergroups (3 id text,4 name text,5 handle text,6 description text,7 user_count integer8)9server slack_server10options (11 resource 'usergroups'12);1314create foreign table slack.usergroup_members (15 usergroup_id text,16 usergroup_name text,17 usergroup_handle text,18 user_id text19)20server slack_server21options (22 resource 'usergroup_members'23);2425-- Get all user groups26select name, handle, description, user_count 27from slack.usergroups 28order by name;2930-- Get all members of a user group (just IDs)31select * from slack.usergroup_members 32where usergroup_id = 'S01234ABCDE';Messages from a Specific Channel#
1create foreign table slack.messages (2 ts text,3 user_id text,4 channel_id text,5 text text,6 thread_ts text,7 reply_count integer8)9server slack_server10options (11 resource 'messages'12);1314-- Get messages from a specific channel15select * from slack.messages 16where channel_id = 'C01234ABCDE'17limit 100;