Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export {
RequestVerificationOptions,
} from './receivers/HTTPModuleFunctions';
export type { HTTPReceiverOptions } from './receivers/HTTPReceiver';
export type { ParamsIncomingMessage, QueryDictionary } from './receivers/ParamsIncomingMessage';
export { HTTPResponseAck } from './receivers/HTTPResponseAck';
export {
defaultProcessEventErrorHandler,
Expand Down
15 changes: 13 additions & 2 deletions src/receivers/HTTPReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import type { BufferedIncomingMessage } from './BufferedIncomingMessage';
import { buildReceiverRoutes, type CustomRoute, type ReceiverRoutes } from './custom-routes';
import * as httpFunc from './HTTPModuleFunctions';
import { HTTPResponseAck } from './HTTPResponseAck';
import type { ParamsIncomingMessage } from './ParamsIncomingMessage';
import type { ParamsIncomingMessage, QueryDictionary } from './ParamsIncomingMessage';
import { verifyRedirectOpts } from './verify-redirect-opts';

// Option keys for tls.createServer() and tls.createSecureContext(), exclusive of those for http.createServer()
Expand Down Expand Up @@ -413,7 +413,9 @@ export default class HTTPReceiver implements Receiver {
const pathMatch = matchRegex(path);
if (pathMatch && this.routes[route][method] !== undefined) {
const params = pathMatch.params as ParamsDictionary;
const message: ParamsIncomingMessage = Object.assign(req, { params });
const parsedUrl = new URL(req.url as string, 'http://localhost');
const query = parseSearchParams(parsedUrl.searchParams);
const message: ParamsIncomingMessage = Object.assign(req, { params, query });
return this.routes[route][method](message, res);
}
}
Expand Down Expand Up @@ -567,3 +569,12 @@ export default class HTTPReceiver implements Receiver {
}
}
}

function parseSearchParams(searchParams: URLSearchParams): QueryDictionary {
const query: QueryDictionary = {};
for (const key of searchParams.keys()) {
const values = searchParams.getAll(key);
query[key] = values.length === 1 ? values[0] : values;
}
return query;
}
9 changes: 9 additions & 0 deletions src/receivers/ParamsIncomingMessage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { IncomingMessage } from 'node:http';
import type { ParamsDictionary } from 'express-serve-static-core';

export type QueryDictionary = Record<string, string | string[] | undefined>;

export interface ParamsIncomingMessage extends IncomingMessage {
/**
* **Only valid for requests with path parameters.**
Expand All @@ -10,4 +12,11 @@ export interface ParamsIncomingMessage extends IncomingMessage {
* then `request.params` will be `{ id: '123' }`.
*/
params?: ParamsDictionary;

/**
* The query parameters of the request. For example, if the request URL is
* `/greetings?name=you&tags=a&tags=b`, then `request.query` will be
* `{ name: 'you', tags: ['a', 'b'] }`.
*/
query?: QueryDictionary;
}
17 changes: 14 additions & 3 deletions src/receivers/SocketModeReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { CodedError } from '../errors';
import type { Receiver, ReceiverEvent } from '../types';
import type { StringIndexed } from '../types/utilities';
import { buildReceiverRoutes, type ReceiverRoutes } from './custom-routes';
import type { ParamsIncomingMessage } from './ParamsIncomingMessage';
import type { ParamsIncomingMessage, QueryDictionary } from './ParamsIncomingMessage';
import {
defaultProcessEventErrorHandler,
type SocketModeReceiverProcessEventErrorHandlerArgs,
Expand Down Expand Up @@ -209,15 +209,17 @@ export default class SocketModeReceiver implements Receiver {
if (customRoutes.length && req.url) {
// NOTE: the domain and scheme are irrelevant here.
// The URL object is only used to safely obtain the path to match
const { pathname: path } = new URL(req.url as string, 'http://localhost');
const parsedUrl = new URL(req.url as string, 'http://localhost');
const { pathname: path } = parsedUrl;
const routes = Object.keys(this.routes);
for (let i = 0; i < routes.length; i += 1) {
const route = routes[i];
const matchRegex = match(route, { decode: decodeURIComponent });
const pathMatch = matchRegex(path);
if (pathMatch && this.routes[route][method] !== undefined) {
const params = pathMatch.params as ParamsDictionary;
const message: ParamsIncomingMessage = Object.assign(req, { params });
const query = parseSearchParams(parsedUrl.searchParams);
const message: ParamsIncomingMessage = Object.assign(req, { params, query });
this.routes[route][method](message, res);
return;
}
Expand Down Expand Up @@ -291,3 +293,12 @@ export default class SocketModeReceiver implements Receiver {
});
}
}

function parseSearchParams(searchParams: URLSearchParams): QueryDictionary {
const query: QueryDictionary = {};
for (const key of searchParams.keys()) {
const values = searchParams.getAll(key);
query[key] = values.length === 1 ? values[0] : values;
}
return query;
}