Skip to content
Merged
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
3 changes: 2 additions & 1 deletion nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
]
},
"useNxCloud": false,
"defaultBase": "main"
"defaultBase": "main",
"analytics": false
}
824 changes: 419 additions & 405 deletions package-lock.json

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
},
"private": true,
"dependencies": {
"@aws-sdk/client-cognito-identity-provider": "^3.929.0",
"@aws-sdk/client-dynamodb": "^3.929.0",
"@aws-sdk/client-eventbridge": "^3.929.0",
"@aws-sdk/client-rds-data": "^3.929.0",
"@aws-sdk/client-s3": "^3.929.0",
"@aws-sdk/client-ses": "^3.929.0",
"@aws-sdk/client-sesv2": "^3.971.0",
"@aws-sdk/lib-dynamodb": "^3.929.0",
"@aws-sdk/s3-request-presigner": "^3.929.0",
"@aws-sdk/client-cognito-identity-provider": "^3.1038.0",
"@aws-sdk/client-dynamodb": "^3.1038.0",
"@aws-sdk/client-eventbridge": "^3.1038.0",
"@aws-sdk/client-rds-data": "^3.1038.0",
"@aws-sdk/client-s3": "^3.1038.0",
"@aws-sdk/client-ses": "^3.1038.0",
"@aws-sdk/client-sesv2": "^3.1038.0",
"@aws-sdk/lib-dynamodb": "^3.1038.0",
"@aws-sdk/s3-request-presigner": "^3.1038.0",
"@middy/core": "~7.3.1",
"@middy/do-not-wait-for-empty-event-loop": "~7.3.1",
"@middy/http-cors": "~7.3.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"name": "@overlap/serverless-tools-api",
"description": "Tools and utilities for working with APIGateway.",
"version": "0.1.0",
"main": "./src/index.js",
"types": "./src/index.d.ts",
Comment on lines +5 to +6
"repository": {
"type": "git",
"url": "https://github.com/overlap-dev/serverless-tools",
Expand Down
51 changes: 43 additions & 8 deletions packages/api/src/lib/apiHandler/apiHandler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
import { MiddyfiedHandler } from '@middy/core';
import { APIGatewayProxyEventV2, Context, Handler } from 'aws-lambda';
import {
APIGatewayProxyEventV2,
APIGatewayProxyResultV2,
Context,
} from 'aws-lambda';
import { formatJSONResponse } from '../formatJsonResponse';
import { logRequest } from '../logRequest';
import { middyfy } from '../middleware';
import { preflightCors } from './preflightCors';

/**
* The result a `RouteController` may return.
*
* Mirrors what API Gateway / Lambda Function URL accept: either a structured
* proxy result (status code, headers, body, ...) or a plain string body.
*/
export type RouteControllerResult = APIGatewayProxyResultV2;

/**
* A function-style route controller.
*
* Notes:
* - The signature is intentionally restricted to **2 parameters**. AWS Lambda's
* Node.js 24 runtime rejects handlers whose `.length === 3` and which are
* not declared `async` (`Runtime.CallbackHandlerDeprecated`). Disallowing
* the 3rd `callback` parameter at the type level prevents that footgun.
* - `event` is typed as `any` on purpose: route controllers are commonly
* produced by `HttpApiEventHandler<TBody, TQuery>`, which narrows `event`
* to a `ValidatedHttpApiProxyEvent`. Because function parameters are
* contravariant, a narrower-event handler can't be assigned to a wider-event
* parameter type — so we accept `any` here and let each controller declare
* its own precise event shape.
*/
export type RouteControllerFn = (
event: any,
context: Context,
) => Promise<RouteControllerResult> | RouteControllerResult;
/**
* Anything that can be registered as a route controller in
* {@link ApiHandlerOptions.routeControllers}: either a plain async function or
* a middy-wrapped handler.
*/
export type RouteController =
| Handler
| RouteControllerFn
| MiddyfiedHandler<any, any, Error, Context>;

export type ApiHandlerOptions = {
Expand All @@ -31,11 +67,10 @@ export const getApiHandler = (options?: ApiHandlerOptions) => {
...opts.routeControllers,
};

const apiHandler: Handler<APIGatewayProxyEventV2> = async (
event,
context,
callback,
) => {
const apiHandler = async (
event: APIGatewayProxyEventV2,
context: Context,
): Promise<RouteControllerResult> => {
logRequest(event);

let routeKey = event.routeKey;
Expand All @@ -50,7 +85,7 @@ export const getApiHandler = (options?: ApiHandlerOptions) => {

// Return controller result or return 404 if no controller was found.
return controller
? controller(event, context, callback)
? controller(event, context)
: middyfy({
handler: async () =>
formatJSONResponse(404, { message: 'Not Found' }),
Expand Down
12 changes: 7 additions & 5 deletions packages/api/src/lib/types/apiGateway.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
APIGatewayProxyEventV2,
APIGatewayProxyStructuredResultV2,
Handler,
Context,
} from 'aws-lambda';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';

Expand All @@ -13,10 +13,12 @@ export type ValidatedHttpApiProxyEvent<
queryStringParameters: FromSchema<TQuery>;
};

// 2-arg async signature only — never the legacy callback form, which
// AWS Lambda's Node.js 24 runtime rejects (Runtime.CallbackHandlerDeprecated).
export type HttpApiEventHandler<
TBody extends JSONSchema,
TQuery extends JSONSchema,
> = Handler<
ValidatedHttpApiProxyEvent<TBody, TQuery>,
APIGatewayProxyStructuredResultV2
>;
> = (
event: ValidatedHttpApiProxyEvent<TBody, TQuery>,
context: Context,
) => Promise<APIGatewayProxyStructuredResultV2>;
3 changes: 2 additions & 1 deletion packages/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"module": "esnext",
"moduleResolution": "bundler",
Comment on lines +4 to +5
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
Expand Down
Loading