Using SST Components for Lambda Functions
Why You Should Consider SST Components for Lambda Functions
Serverless computing has revolutionized how developers build applications. AWS Lambda for serverless architectures enables rapid development, reduced costs, and seamless scalability.
However, managing and deploying Lambda functions can become cumbersome without a streamlined framework. Today, there are many frameworks and tools to solve these challenges. SST (Serverless Stack) offers a powerful solution that works best for Lambda in terms of cost and optimization.
Yet, it's not fully complete when structuring the Lambda logic, where Bit components can bridge the gap.
This article explores the benefits of using SST with Bit components for AWS Lambda functions, detailing their impact on development workflows, infrastructure management, and scalability.
It also works well when SST hosts the frontend components in Lambda (e.g. when NextJS + SST is used).
What Are SST Components?
SST Components are building blocks that simplify serverless application development. They provide a unified way to define, manage, and deploy serverless resources such as Lambda functions, APIs, and DynamoDB tables. Unlike traditional IaC (Infrastructure as Code) tools, SST components integrate application code with infrastructure configuration, streamlining the development lifecycle.
The Case for SST and Bit
SST simplifies serverless application development by abstracting AWS infrastructure and focusing on code and deployment efficiency. For example, an SST component can represent a Lambda function with all its configurations (e.g., environment variables, IAM roles, triggers):
import * as sst from "@serverless-stack/resources";
export default function MyStack({ stack }) {
const lambda = new sst.Function(stack, "MyFunction", {
handler: "functions/my-function.handler",
});
stack.addOutputs({
FunctionArn: lambda.functionArn,
});
}This approach reduces boilerplate and integrates infrastructure management with application logic, improving consistency and developer productivity.
Bit Components: Modularizing Lambda Functions
Bit takes SST’s modularity further by transforming Lambda functions and their dependencies into standalone components. A Bit component encapsulates all logic, configuration, and testing for a Lambda function, enabling it to:
- Be Independently Versioned: Each Lambda function or dependency is independently versioned and reusable across other functions.
- Live in Multiple Repositories: Components are not tied to a single repository or monorepo. They can be shared across distributed repositories or even across organizations.
- Enhance Change Tracking: Changes to a Lambda function or its dependencies are automatically detected and tracked, ensuring teams know all modifications before deployment.
- Improved Modularity and Testing: Each Bit component is independently testable, making debugging and quality assurance more manageable. This allows testing of the individual units inside the Lambada function logic.
Lambda SST and Bit Components: Modular and Reusable Logic
Reusable components for cross-cutting concerns like sanitization, validation, and authorization are powerful tools in modern serverless development. These components, stored on Bit Platform, are versioned and can be reused across Lambda functions, ensuring consistency and modularity.
Note: Instead of importing from local paths, these components are imported from a Bit organization and namespace, similar to an npm package, where each component has its own lifecycle.
Suppose you need to create two Lambda functions, CreateUser and AuthenticateUser, both reuse the shared components for sanitization, validation, and authorization.
functions/
createUser/
handler.ts
index.ts
authenticateUser/
handler.ts
index.ts
shared/
sanitization/
sanitization.ts
index.ts
validation/
validation.ts
index.ts
authorization/
authorization.ts
index.tsFor example, the Sanitization Component and Validation Component may look as follows.
// # Validation Component
// @file: shared/sanitization/sanitization.ts
export const sanitizeInput = (input: any): any => {
// Example sanitization logic
return {
...input,
sanitized: true,
};
};// # Sanitization Component
// @file: shared/validation/validation.ts
export const validateInput = (input: any): boolean => {
if (!input || !input.requiredField) {
throw new Error("Validation failed: Missing requiredField");
}
return true;
};Now, let's look at how we can structure the lambda functions.
// # Create User Lambda Componenent
// @file: functions/createUser/handler.ts
import { sanitizeInput } from "@org.utils/sanitization";
import { validateInput } from "@org.utils/validation";
import { authorizeRequest } from "@org.utils/authorization";
import { createUserInDB } from "@org.business/create-user";
export const handler = async (event: any) => {
// Authorize the request
authorizeRequest(event.headers);
// Parse and sanitize the input
const body = JSON.parse(event.body || "{}");
const sanitizedInput = sanitizeInput(body);
// Validate the sanitized input
validateInput(sanitizedInput);
// Perform user creation logic
const createdUser = createUserInDB(sanitizedInput);
return {
statusCode: 201,
body: JSON.stringify({
message: "User created successfully!",
user: createdUser,
}),
};
};// # Authenticate User Lambda Component
// @file: functions/createUser/handler.ts
import { sanitizeInput } from "@org.utils/sanitization";
import { validateInput } from "@org.utils/validation";
import { authorizeRequest } from "@org.utils/authorization";
export const handler = async (event: any) => {
// Authorize the request
authorizeRequest(event.headers);
// Parse and sanitize the input
const body = JSON.parse(event.body || "{}");
const sanitizedInput = sanitizeInput(body);
// Validate the sanitized input
validateInput(sanitizedInput);
// Perform authentication logic (mocked for this example)
const isAuthenticated = sanitizedInput.username === "admin" && sanitizedInput.password === "password";
if (!isAuthenticated) {
return {
statusCode: 401,
body: JSON.stringify({ message: "Authentication failed" }),
};
}
return {
statusCode: 200,
body: JSON.stringify({
message: "Authentication successful",
token: "fake-jwt-token",
}),
};
};Breaking the Lambda function into components makes the overall function robust. It speeds up the development speed since we can interest fast on business logic, even without deploying the code into the AWS cloud.
- Modular Business Logic: Components like
createUserInDBencapsulate business-specific logic, ensuring a clear separation of concerns. - Cross-Cutting Component Reuse: Authorization, validation, and sanitization logic are consistent across functions, improving maintainability.
- Dependency Management with Bit:
- Bit tracks dependencies across functions, ensuring the impact of changes to shared components is visible.
- All reusable components are imported via the Bit Cloud registry (e.g.,
@org.utils/sanitization).
Conclusion
Overall, this structure promotes scalability and maintainability for complex Lambda-based serverless applications.
Get Ashan Fernando’s stories in your inbox
Join Medium for free to get updates from this writer.
Structuring Lambda functions as SST components and more granular business logic components with Bit helps to manage dependency management. You can:
- Centralize and reuse cross-cutting logic like sanitization, validation, and authorization.
- Manage complex dependency graphs.
Thank you for reading! Cheers!










