lambder 1.0.65 → 1.0.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +102 -25
- package/dist/Lambder.d.ts +12 -12
- package/package.json +1 -1
- package/src/Lambder.ts +24 -24
package/Readme.md
CHANGED
|
@@ -1,58 +1,135 @@
|
|
|
1
1
|
# Lambder
|
|
2
2
|
|
|
3
|
-
Lambder
|
|
3
|
+
Lambder is a dynamic serverless framework designed to facilitate the management and implementation of routes and APIs within AWS Lambda functions, specifically tailored for TypeScript projects. It provides a streamlined approach to handling HTTP requests, managing sessions, and defining API routes, making serverless application development more intuitive and structured.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Response helpers for JSON, HTML, XML, files, and more.
|
|
7
|
+
- **Simple API & Route Declaration**: Define your APIs and routes using concise and expressive syntax.
|
|
8
|
+
- **Session Management**: Built-in session management to secure and personalize user experiences.
|
|
9
|
+
- **Flexible Hooks System**: Employ hooks to execute code at different stages of the request lifecycle, enabling fine-grained control over the application flow.
|
|
10
|
+
- **Error Handling**: Comprehensive error handling capabilities, including global error handlers and route-specific fallbacks.
|
|
11
|
+
- **Seamless Integration**: Designed to work effortlessly with AWS Lambda and API Gateway, providing a straightforward path to deploy serverless applications.
|
|
13
12
|
|
|
14
13
|
## Installation
|
|
15
14
|
|
|
15
|
+
To include Lambder in your TypeScript project, you can install it using npm or yarn. First, ensure that you have TypeScript set up in your project.
|
|
16
|
+
|
|
16
17
|
```bash
|
|
17
18
|
npm install lambder
|
|
19
|
+
# or
|
|
20
|
+
yarn add lambder
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
##
|
|
23
|
+
## Backend Usage
|
|
24
|
+
|
|
25
|
+
### Basic Setup
|
|
21
26
|
|
|
22
27
|
```typescript
|
|
23
28
|
import Lambder from 'lambder';
|
|
24
|
-
import
|
|
29
|
+
import * as path from 'path';
|
|
25
30
|
|
|
26
|
-
const
|
|
27
|
-
apiPath:
|
|
28
|
-
publicPath: '/public',
|
|
31
|
+
const lambder = new Lambder({
|
|
32
|
+
apiPath: "/secure",
|
|
29
33
|
isCorsEnabled: true,
|
|
34
|
+
publicPath: path.resolve(`./public`),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Define a simple api
|
|
38
|
+
lambder.addApi("fetchData", async (ctx, res) => {
|
|
39
|
+
const data = await fetchDataSomehow();
|
|
40
|
+
return res.api({ payload: data });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Define a simple route
|
|
44
|
+
lambder.addRoute("/hello-world", (ctx, res) => {
|
|
45
|
+
return res.json({ message: "Hello World" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Match all other paths and serve static files from publicPath, if not found, serve index.html
|
|
49
|
+
lambder.addRoute("*", (ctx, res)=>{
|
|
50
|
+
return res.file(ctx.path, {}, "index.html");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// Set a fallback handler for unmatched routes
|
|
55
|
+
lambder.setRouteFallbackHandler((ctx, res) => {
|
|
56
|
+
return res.status404("Not Found");
|
|
30
57
|
});
|
|
31
58
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
59
|
+
// Global error handler
|
|
60
|
+
lambder.setGlobalErrorHandler((err, ctx, res) => {
|
|
61
|
+
console.error("Error:", err);
|
|
62
|
+
return res.status500("Internal Server Error");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const handler = (event, context) => {
|
|
66
|
+
return lambder.render(event, context);
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Adding Hooks
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Before render hook example
|
|
74
|
+
lambder.addHook("beforeRender", async (ctx, res) => {
|
|
75
|
+
// Perform actions before rendering
|
|
76
|
+
if(ctx.method === "POST") {
|
|
77
|
+
// Validate CSRF token, for example
|
|
78
|
+
}
|
|
79
|
+
return ctx; // Return modified context or throw an Error
|
|
35
80
|
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Frontend Usage with LambderCaller
|
|
84
|
+
|
|
85
|
+
LambderCaller is a frontend companion library for Lambder, designed to simplify making API requests to your Lambder backend services.
|
|
36
86
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
87
|
+
### Installing LambderCaller
|
|
88
|
+
|
|
89
|
+
LambderCaller is included in the same `lambder` package. Ensure you have `lambder` available in your frontend project.
|
|
90
|
+
|
|
91
|
+
### Basic Setup
|
|
92
|
+
|
|
93
|
+
Begin by initializing LambderCaller with your API configuration. This setup assumes your project structure accommodates a place for initiating and configuring API handlers, possibly within a dedicated JavaScript module or directly in your main application file.
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
import LambderCaller from "lambder/dist/LambderCaller";
|
|
97
|
+
|
|
98
|
+
const lambderCaller = new LambderCaller({
|
|
99
|
+
isCorsEnabled: false,
|
|
100
|
+
apiPath: "/secure", // Your Lambder API endpoint, must be the same as in your backend
|
|
101
|
+
fetchStartedHandler: ({ activeFetchList }) => {
|
|
102
|
+
// When any api call starts
|
|
103
|
+
},
|
|
104
|
+
fetchEndedHandler: ({ activeFetchList }) => {
|
|
105
|
+
// When any api call ends
|
|
106
|
+
},
|
|
107
|
+
errorMessageHandler: (message) => {
|
|
108
|
+
console.error("LambderCaller:", message); // Handle error messages
|
|
109
|
+
},
|
|
40
110
|
});
|
|
41
111
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
112
|
+
|
|
113
|
+
const loadPageData = async () => {
|
|
114
|
+
try {
|
|
115
|
+
const response = await lambderCaller.api("getCompanyPage", {
|
|
116
|
+
subdomain: "example", // Pass necessary parameters for your API call
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error("Failed to fetch page data:", error);
|
|
120
|
+
}
|
|
45
121
|
};
|
|
122
|
+
|
|
46
123
|
```
|
|
47
124
|
|
|
48
|
-
##
|
|
125
|
+
## Advanced Configuration
|
|
49
126
|
|
|
50
|
-
|
|
127
|
+
Lambder is designed to be flexible and extensible, allowing for customized behaviors through hooks and custom error handling mechanisms.
|
|
51
128
|
|
|
52
129
|
## Contributing
|
|
53
130
|
|
|
54
|
-
Contributions are welcome!
|
|
131
|
+
Contributions are welcome! Especially for documentation. If you have an idea for an improvement or have found a bug, please open an issue or submit a pull request.
|
|
55
132
|
|
|
56
133
|
## License
|
|
57
134
|
|
|
58
|
-
MIT License
|
|
135
|
+
This project is licensed under the [MIT License](LICENSE).
|
package/dist/Lambder.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { APIGatewayProxyEvent, APIGatewayProxyEventHeaders, Context } from
|
|
|
2
2
|
import LambderResolver from "./LambderResolver.js";
|
|
3
3
|
import LambderResponseBuilder, { LambderResolverResponse } from "./LambderResponseBuilder.js";
|
|
4
4
|
type Path = `/${string}`;
|
|
5
|
-
type
|
|
5
|
+
type LambderSession = {
|
|
6
6
|
userId: string;
|
|
7
7
|
userIdHash: string;
|
|
8
8
|
sessionHash: string;
|
|
@@ -11,7 +11,7 @@ type Session = {
|
|
|
11
11
|
expiresTimeStamp: number;
|
|
12
12
|
data: Record<string, any>;
|
|
13
13
|
};
|
|
14
|
-
type
|
|
14
|
+
type LambderRenderContext = {
|
|
15
15
|
host: string;
|
|
16
16
|
path: string;
|
|
17
17
|
pathParams: Record<string, any> | null;
|
|
@@ -21,19 +21,19 @@ type RenderContext = {
|
|
|
21
21
|
cookie: Record<string, any>;
|
|
22
22
|
apiName: string;
|
|
23
23
|
headers: APIGatewayProxyEventHeaders;
|
|
24
|
-
session:
|
|
24
|
+
session: LambderSession | null;
|
|
25
25
|
lambdaContext: Context;
|
|
26
26
|
};
|
|
27
|
-
type ConditionFunction = (ctx:
|
|
28
|
-
type ActionFunction = (ctx:
|
|
27
|
+
type ConditionFunction = (ctx: LambderRenderContext) => boolean;
|
|
28
|
+
type ActionFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse | Promise<LambderResolverResponse>;
|
|
29
29
|
type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
|
|
30
|
-
type HookBeforeRenderFunction = (ctx:
|
|
31
|
-
type HookAfterRenderFunction = (ctx:
|
|
32
|
-
type HookFallbackFunction = (ctx:
|
|
33
|
-
type GlobalErrorHandlerFunction = (err: Error, ctx:
|
|
34
|
-
type RouteFallbackHandlerFunction = (ctx:
|
|
35
|
-
type ApiFallbackHandlerFunction = (ctx:
|
|
36
|
-
export declare const createContext: (event: APIGatewayProxyEvent, lambdaContext: Context, apiPath?: string | null) =>
|
|
30
|
+
type HookBeforeRenderFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderRenderContext | Error | Promise<LambderRenderContext | Error>;
|
|
31
|
+
type HookAfterRenderFunction = (ctx: LambderRenderContext, resolver: LambderResolver, response: LambderResolverResponse) => LambderResolverResponse | Error | Promise<LambderResolverResponse | Error>;
|
|
32
|
+
type HookFallbackFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => void | Promise<void>;
|
|
33
|
+
type GlobalErrorHandlerFunction = (err: Error, ctx: LambderRenderContext | null, response: LambderResponseBuilder) => LambderResolverResponse | Promise<LambderResolverResponse>;
|
|
34
|
+
type RouteFallbackHandlerFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse;
|
|
35
|
+
type ApiFallbackHandlerFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse;
|
|
36
|
+
export declare const createContext: (event: APIGatewayProxyEvent, lambdaContext: Context, apiPath?: string | null) => LambderRenderContext;
|
|
37
37
|
export default class Lambder {
|
|
38
38
|
private apiPath;
|
|
39
39
|
private apiVersion;
|
package/package.json
CHANGED
package/src/Lambder.ts
CHANGED
|
@@ -8,7 +8,7 @@ import LambderResponseBuilder, { LambderResolverResponse } from "./LambderRespon
|
|
|
8
8
|
|
|
9
9
|
type Path = `/${string}`;
|
|
10
10
|
|
|
11
|
-
type
|
|
11
|
+
type LambderSession = {
|
|
12
12
|
userId: string,
|
|
13
13
|
userIdHash: string;
|
|
14
14
|
sessionHash: string;
|
|
@@ -18,7 +18,7 @@ type Session = {
|
|
|
18
18
|
data: Record<string, any>;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
type
|
|
21
|
+
type LambderRenderContext = {
|
|
22
22
|
host: string;
|
|
23
23
|
path: string;
|
|
24
24
|
pathParams: Record<string, any> | null;
|
|
@@ -28,30 +28,30 @@ type RenderContext = {
|
|
|
28
28
|
cookie: Record<string, any>;
|
|
29
29
|
apiName: string;
|
|
30
30
|
headers: APIGatewayProxyEventHeaders;
|
|
31
|
-
session:
|
|
31
|
+
session: LambderSession|null;
|
|
32
32
|
lambdaContext: Context;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
type ConditionFunction = (ctx:
|
|
36
|
-
type ActionFunction = (ctx:
|
|
35
|
+
type ConditionFunction = (ctx: LambderRenderContext) => boolean;
|
|
36
|
+
type ActionFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse|Promise<LambderResolverResponse>;
|
|
37
37
|
type ActionObject = { conditionFn: ConditionFunction, actionFn: ActionFunction };
|
|
38
38
|
|
|
39
|
+
type HookEventType = "created"|"beforeRender"|"afterRender"|"fallback";
|
|
39
40
|
type HookCreatedFunction = (lambderInstance: Lambder) => Promise<void>;
|
|
40
|
-
type HookBeforeRenderFunction = (ctx:
|
|
41
|
-
type HookAfterRenderFunction = (ctx:
|
|
42
|
-
type HookFallbackFunction = (ctx:
|
|
41
|
+
type HookBeforeRenderFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => LambderRenderContext|Error|Promise<LambderRenderContext|Error>;
|
|
42
|
+
type HookAfterRenderFunction = (ctx: LambderRenderContext, resolver: LambderResolver, response: LambderResolverResponse) => LambderResolverResponse|Error|Promise<LambderResolverResponse|Error>;
|
|
43
|
+
type HookFallbackFunction = (ctx: LambderRenderContext, resolver: LambderResolver) => void|Promise<void>;
|
|
43
44
|
|
|
44
|
-
type GlobalErrorHandlerFunction = (err: Error, ctx:
|
|
45
|
-
type RouteFallbackHandlerFunction = (ctx:
|
|
46
|
-
type ApiFallbackHandlerFunction = (ctx:
|
|
45
|
+
type GlobalErrorHandlerFunction = (err: Error, ctx: LambderRenderContext|null, response: LambderResponseBuilder) => LambderResolverResponse|Promise<LambderResolverResponse>;
|
|
46
|
+
type RouteFallbackHandlerFunction = (ctx:LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse;
|
|
47
|
+
type ApiFallbackHandlerFunction = (ctx:LambderRenderContext, resolver: LambderResolver) => LambderResolverResponse;
|
|
47
48
|
|
|
48
|
-
type HookEventType = "created"|"beforeRender"|"afterRender"|"fallback";
|
|
49
49
|
|
|
50
50
|
export const createContext = (
|
|
51
51
|
event: APIGatewayProxyEvent,
|
|
52
52
|
lambdaContext: Context,
|
|
53
53
|
apiPath: string|null = null
|
|
54
|
-
):
|
|
54
|
+
):LambderRenderContext => {
|
|
55
55
|
const host = event.headers.Host || event.headers.host || "";
|
|
56
56
|
const path = event.path;
|
|
57
57
|
const pathParams = null;
|
|
@@ -123,7 +123,7 @@ export default class Lambder {
|
|
|
123
123
|
return (match(pattern, { decode: decodeURIComponent }))(path) !== false;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
private async validateSession (ctx:
|
|
126
|
+
private async validateSession (ctx: LambderRenderContext): Promise<boolean>{
|
|
127
127
|
const { session, apiName, post, cookie } = ctx;
|
|
128
128
|
if(!session) return false;
|
|
129
129
|
if(!session.userId || !session.userIdHash || !session.sessionHash || !session.csrfToken) return false;
|
|
@@ -139,7 +139,7 @@ export default class Lambder {
|
|
|
139
139
|
return false;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
private async handleNoMatchedAction(ctx:
|
|
142
|
+
private async handleNoMatchedAction(ctx: LambderRenderContext, resolver: LambderResolver){
|
|
143
143
|
for(const hook of this.hookList["fallback"]){ await hook.hookFn(ctx, resolver); }
|
|
144
144
|
|
|
145
145
|
const isAPI = ctx.path === this.apiPath;
|
|
@@ -159,7 +159,7 @@ export default class Lambder {
|
|
|
159
159
|
}
|
|
160
160
|
addRoute(condition: Path|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
161
161
|
this.actionList.push({
|
|
162
|
-
conditionFn: (ctx:
|
|
162
|
+
conditionFn: (ctx:LambderRenderContext) => (
|
|
163
163
|
ctx.method === "GET" &&
|
|
164
164
|
(
|
|
165
165
|
(typeof condition === "string" && this.testPatternMatch(condition, ctx.path)) ||
|
|
@@ -167,7 +167,7 @@ export default class Lambder {
|
|
|
167
167
|
(condition?.constructor == RegExp && condition.test(ctx.path))
|
|
168
168
|
)
|
|
169
169
|
),
|
|
170
|
-
actionFn: async (ctx:
|
|
170
|
+
actionFn: async (ctx:LambderRenderContext, resolver: LambderResolver) => {
|
|
171
171
|
if(typeof condition === "string"){
|
|
172
172
|
ctx.pathParams = this.getPatternMatch(condition, ctx.path);
|
|
173
173
|
}else if(condition?.constructor == RegExp){
|
|
@@ -181,7 +181,7 @@ export default class Lambder {
|
|
|
181
181
|
|
|
182
182
|
addSessionRoute(condition: Path|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
183
183
|
this.actionList.push({
|
|
184
|
-
conditionFn: (ctx:
|
|
184
|
+
conditionFn: (ctx:LambderRenderContext) => (
|
|
185
185
|
ctx.method === "GET" &&
|
|
186
186
|
(
|
|
187
187
|
(typeof condition === "string" && this.testPatternMatch(condition, ctx.path)) ||
|
|
@@ -189,7 +189,7 @@ export default class Lambder {
|
|
|
189
189
|
(condition?.constructor == RegExp && condition.test(ctx.path))
|
|
190
190
|
)
|
|
191
191
|
),
|
|
192
|
-
actionFn: async (ctx:
|
|
192
|
+
actionFn: async (ctx:LambderRenderContext, resolver: LambderResolver) => {
|
|
193
193
|
const isSessionValid = this.validateSession(ctx);
|
|
194
194
|
if(!isSessionValid) throw new Error("Session not found");
|
|
195
195
|
if(typeof condition === "string"){
|
|
@@ -204,23 +204,23 @@ export default class Lambder {
|
|
|
204
204
|
|
|
205
205
|
addApi(apiName: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
206
206
|
this.actionList.push({
|
|
207
|
-
conditionFn: (ctx:
|
|
207
|
+
conditionFn: (ctx:LambderRenderContext) => (
|
|
208
208
|
(typeof apiName === "string" && ctx.apiName === apiName) ||
|
|
209
209
|
(typeof apiName === "function" && apiName(ctx)) ||
|
|
210
210
|
(apiName?.constructor == RegExp && apiName.test(ctx.apiName))
|
|
211
211
|
),
|
|
212
|
-
actionFn: async (ctx:
|
|
212
|
+
actionFn: async (ctx:LambderRenderContext, resolver: LambderResolver) => await actionFn(ctx, resolver),
|
|
213
213
|
});
|
|
214
214
|
};
|
|
215
215
|
|
|
216
216
|
addSessionApi(apiName: string|ConditionFunction|RegExp, actionFn: ActionFunction):void{
|
|
217
217
|
this.actionList.push({
|
|
218
|
-
conditionFn: (ctx:
|
|
218
|
+
conditionFn: (ctx:LambderRenderContext) => (
|
|
219
219
|
(typeof apiName === "string" && ctx.apiName === apiName) ||
|
|
220
220
|
(typeof apiName === "function" && apiName(ctx)) ||
|
|
221
221
|
(apiName?.constructor == RegExp && apiName.test(ctx.apiName))
|
|
222
222
|
),
|
|
223
|
-
actionFn: async (ctx:
|
|
223
|
+
actionFn: async (ctx:LambderRenderContext, resolver: LambderResolver) => {
|
|
224
224
|
const isSessionValid = this.validateSession(ctx);
|
|
225
225
|
if(!isSessionValid) throw new Error("Session not found");
|
|
226
226
|
return await actionFn(ctx, resolver);
|
|
@@ -266,7 +266,7 @@ export default class Lambder {
|
|
|
266
266
|
event: APIGatewayProxyEvent,
|
|
267
267
|
lambdaContext: Context
|
|
268
268
|
): Promise<LambderResolverResponse>{
|
|
269
|
-
let eventRenderContext:
|
|
269
|
+
let eventRenderContext:LambderRenderContext|null = null;
|
|
270
270
|
|
|
271
271
|
try {
|
|
272
272
|
let ctx = createContext(event, lambdaContext);
|