Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return types #44

Merged
merged 3 commits into from
Nov 2, 2023
Merged

Return types #44

merged 3 commits into from
Nov 2, 2023

Conversation

RobinBuschmann
Copy link
Contributor

@RobinBuschmann RobinBuschmann commented Nov 1, 2023

thirty now adds type safety for return types of all middlewares and its actual handler. Previously the return type of a composed handler was Promise<any>. This allows a lot more features and makes thirty in a whole more type-safe.

Usage

const handler = compose(
-  eventType<APIGatewayEvent>(),
+  types<APIGatewayEvent, Promise<APIGatewayProxyResult>>(),
)(async event => {
  // ...
});

Additional changes

  • Drops createRoutes helper
  • Renames Handler to Next
  • Introduces forEachSqsRecord middleware
  • Introduces serializeJson middleware

forEachSqsRecord

Consider the following setup not using that middleware:

type SomeMesssage = {id: string; text: string};
const handler = compose(
  types<SQSEvent, Promise<SQSBatchResponse>>(),
)(async (event) => {
  return {
    batchItemFailures: (
      await Promise.all(
        event.Records.map((record) => {
          try {
            const message: SomeMessage = JSON.parse(record.body);
            // process message
          } catch (e) {
            return {
              itemIdentifier: record.messageId,
            };
          }
        }),
      )
    ).filter((maybeItemFailure): maybeItemFailure is SQSBatchItemFailure => !!maybeItemFailure),
  };
});

You have to do a lot of boilerplate code, which makes the actual business code of processing one message hard to read. forEachSqsRecord lets you process one message without any of that boilerplate:

const handler = compose(
  types<SQSEvent, Promise<SQSBatchResponse>>(),
  forEachSqsRecord({
    batchItemFailures: true,
    bodyType: of<SomeMessage>,
  })
)(async (event) => {
  const message = event.record.body;
  // process message
});

serializeJson

Before that middleware you had to serialize your response body's manually and parse it back again in your tests in order to assert response bodys - especially partially.

type User = {id: string; name: string};
const handler = compose(
  types<APIGatewayEvent, Promise<APIGatewayProxyResult>>(),
)(async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({id: 'USER_1', name: 'Marty'} as User),
  };
});

const response = await handler.actual({...});
expect(response.statusCode).toEqual(200);
expect(JSON.parse(response.body)).toEqual(expect.objectContaining({
     id: 'USER_1'
  }));

But serializeJson makes type-safe and testing less verbose:

const handler = compose(
  types<APIGatewayEvent, Promise<APIGatewayProxyResult>>(),
  serializeJson(of<User>),
)(async (event) => {
  return {
    statusCode: 200,
    body: {id: 'USER_1', name: 'Marty'},
  };
});

const response = await handler.actual({...});
expect(response).toEqual({
   statusCode: 200,
   body: expect.objectContaining({ id: 'USER_1' })
});

@RobinBuschmann RobinBuschmann marked this pull request as ready for review November 2, 2023 10:30
@RobinBuschmann RobinBuschmann merged commit 7ae5c77 into master Nov 2, 2023
2 checks passed
@RobinBuschmann RobinBuschmann deleted the feature/return-types branch November 2, 2023 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant