@unrdf/serverless 26.4.2
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 +305 -0
- package/package.json +81 -0
- package/src/api/api-gateway-config.mjs +390 -0
- package/src/api/index.mjs +12 -0
- package/src/cdk/index.mjs +6 -0
- package/src/cdk/unrdf-stack.mjs +363 -0
- package/src/deploy/index.mjs +10 -0
- package/src/deploy/lambda-bundler.mjs +310 -0
- package/src/index.mjs +91 -0
- package/src/storage/dynamodb-adapter.mjs +324 -0
- package/src/storage/index.mjs +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# @unrdf/serverless
|
|
2
|
+
|
|
3
|
+
> One-click serverless deployment of UNRDF applications to AWS
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@unrdf/serverless` provides infrastructure-as-code and deployment tooling for running UNRDF RDF applications on AWS Lambda with:
|
|
8
|
+
|
|
9
|
+
- **AWS CDK** - Infrastructure as code with TypeScript/JavaScript
|
|
10
|
+
- **Lambda Functions** - Auto-scaling compute for RDF queries
|
|
11
|
+
- **DynamoDB** - Persistent triple storage with optimized indexes
|
|
12
|
+
- **API Gateway** - REST endpoints with CORS and rate limiting
|
|
13
|
+
- **CloudFront CDN** - Global distribution (optional)
|
|
14
|
+
- **esbuild** - Optimized bundling for minimal cold starts
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @unrdf/serverless
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Peer Dependencies
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @aws-sdk/client-dynamodb @aws-sdk/client-lambda
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Create CDK Stack
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { App } from 'aws-cdk-lib';
|
|
34
|
+
import { createUNRDFStack } from '@unrdf/serverless';
|
|
35
|
+
|
|
36
|
+
const app = new App();
|
|
37
|
+
|
|
38
|
+
const stack = createUNRDFStack(app, 'MyUNRDFApp', {
|
|
39
|
+
environment: 'prod',
|
|
40
|
+
memorySizeMb: 2048,
|
|
41
|
+
timeoutSeconds: 30,
|
|
42
|
+
enableCdn: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
app.synth();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Bundle Lambda Functions
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import { LambdaBundler } from '@unrdf/serverless';
|
|
52
|
+
|
|
53
|
+
const bundler = new LambdaBundler({
|
|
54
|
+
entryPoint: './src/handler.mjs',
|
|
55
|
+
outDir: './dist/lambda',
|
|
56
|
+
minify: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const metadata = await bundler.bundle();
|
|
60
|
+
console.log(`Bundle size: ${metadata.sizeBytes} bytes`);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Configure API Gateway
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import { ApiGatewayConfig } from '@unrdf/serverless';
|
|
67
|
+
|
|
68
|
+
const config = new ApiGatewayConfig('my-api', 'prod')
|
|
69
|
+
.addEndpoint('/query', 'POST', 'queryFunction', {
|
|
70
|
+
authRequired: true,
|
|
71
|
+
rateLimit: 100,
|
|
72
|
+
})
|
|
73
|
+
.addEndpoint('/triples', 'GET', 'queryFunction')
|
|
74
|
+
.enableCors(['https://example.com'])
|
|
75
|
+
.build();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 4. Deploy
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Synthesize CloudFormation template
|
|
82
|
+
pnpm cdk:synth
|
|
83
|
+
|
|
84
|
+
# Deploy to AWS
|
|
85
|
+
pnpm cdk:deploy
|
|
86
|
+
|
|
87
|
+
# Destroy infrastructure
|
|
88
|
+
pnpm cdk:destroy
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Architecture
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
┌─────────────────┐
|
|
95
|
+
│ CloudFront │ (Optional CDN)
|
|
96
|
+
│ CDN │
|
|
97
|
+
└────────┬────────┘
|
|
98
|
+
│
|
|
99
|
+
┌────────▼────────┐
|
|
100
|
+
│ API Gateway │ (REST API)
|
|
101
|
+
└────────┬────────┘
|
|
102
|
+
│
|
|
103
|
+
┌────┴─────┬─────────┐
|
|
104
|
+
│ │ │
|
|
105
|
+
┌───▼──┐ ┌──▼──┐ ┌──▼──┐
|
|
106
|
+
│Query │ │List │ │Ingest│ (Lambda Functions)
|
|
107
|
+
│Lambda│ │Lambda│ │Lambda│
|
|
108
|
+
└───┬──┘ └──┬──┘ └──┬──┘
|
|
109
|
+
│ │ │
|
|
110
|
+
└─────────┼────────┘
|
|
111
|
+
│
|
|
112
|
+
┌──────▼──────┐
|
|
113
|
+
│ DynamoDB │ (Triple Store)
|
|
114
|
+
│ Table │
|
|
115
|
+
└─────────────┘
|
|
116
|
+
(SPO, PSO, OSP indexes)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Features
|
|
120
|
+
|
|
121
|
+
### CDK Infrastructure
|
|
122
|
+
|
|
123
|
+
- **Auto-scaling Lambda** - Concurrent execution limits
|
|
124
|
+
- **DynamoDB tables** - Optimized for triple patterns
|
|
125
|
+
- **Global Secondary Indexes** - Fast predicate/object queries
|
|
126
|
+
- **CloudFront CDN** - Global edge caching
|
|
127
|
+
- **X-Ray tracing** - Built-in observability
|
|
128
|
+
|
|
129
|
+
### Lambda Bundling
|
|
130
|
+
|
|
131
|
+
- **esbuild integration** - Fast, optimized builds
|
|
132
|
+
- **Tree-shaking** - Minimal bundle sizes
|
|
133
|
+
- **Automatic externals** - AWS SDK excluded by default
|
|
134
|
+
- **Source maps** - Optional debugging support
|
|
135
|
+
- **Gzip compression** - Deployment optimization
|
|
136
|
+
|
|
137
|
+
### API Gateway
|
|
138
|
+
|
|
139
|
+
- **REST endpoints** - Full CRUD operations
|
|
140
|
+
- **CORS support** - Configurable origins
|
|
141
|
+
- **Rate limiting** - Per-endpoint throttling
|
|
142
|
+
- **Authentication** - API key integration
|
|
143
|
+
- **OpenAPI export** - Auto-generated specs
|
|
144
|
+
|
|
145
|
+
### DynamoDB Storage
|
|
146
|
+
|
|
147
|
+
- **Triple patterns** - SPO, PSO, OSP queries
|
|
148
|
+
- **Global indexes** - Optimized access patterns
|
|
149
|
+
- **Batch operations** - Bulk import support
|
|
150
|
+
- **Pagination** - Large result sets
|
|
151
|
+
- **Point-in-time recovery** - Production safety
|
|
152
|
+
|
|
153
|
+
## Examples
|
|
154
|
+
|
|
155
|
+
### Deploy Demo (Dry-Run)
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Run deployment demo without AWS
|
|
159
|
+
node examples/deploy-demo.mjs --dry-run
|
|
160
|
+
|
|
161
|
+
# Deploy to AWS (requires credentials)
|
|
162
|
+
node examples/deploy-demo.mjs --deploy --env=prod
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom Stack Configuration
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
import { UNRDFStack } from '@unrdf/serverless/cdk';
|
|
169
|
+
|
|
170
|
+
class CustomStack extends UNRDFStack {
|
|
171
|
+
constructor(scope, id, props) {
|
|
172
|
+
super(scope, id, {
|
|
173
|
+
config: {
|
|
174
|
+
environment: 'staging',
|
|
175
|
+
memorySizeMb: 1536,
|
|
176
|
+
timeoutSeconds: 60,
|
|
177
|
+
enableStreaming: true, // Enable DynamoDB streams
|
|
178
|
+
},
|
|
179
|
+
...props,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Add custom resources
|
|
183
|
+
this.addCustomMetrics();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
addCustomMetrics() {
|
|
187
|
+
// Custom CloudWatch metrics, alarms, etc.
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Lambda Handler Example
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
import { createAdapterFromEnv, createApiResponse, createErrorResponse } from '@unrdf/serverless';
|
|
196
|
+
|
|
197
|
+
export async function handler(event) {
|
|
198
|
+
try {
|
|
199
|
+
const adapter = createAdapterFromEnv();
|
|
200
|
+
const body = JSON.parse(event.body);
|
|
201
|
+
|
|
202
|
+
const results = await adapter.queryTriples({
|
|
203
|
+
subject: body.subject,
|
|
204
|
+
predicate: body.predicate,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return createApiResponse(200, { results, count: results.length });
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return createErrorResponse(error, 500);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Bundle Analysis
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
import { LambdaBundler } from '@unrdf/serverless';
|
|
218
|
+
|
|
219
|
+
const analysis = await LambdaBundler.analyzeBundleSize('./dist/metafile.json');
|
|
220
|
+
|
|
221
|
+
console.log('Bundle Analysis:');
|
|
222
|
+
console.log(`Total size: ${analysis.totalSizeBytes} bytes`);
|
|
223
|
+
console.log('Largest dependencies:');
|
|
224
|
+
for (const dep of analysis.largestDeps) {
|
|
225
|
+
console.log(` ${dep.name}: ${dep.bytes} bytes (${dep.percentage}%)`);
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Configuration
|
|
230
|
+
|
|
231
|
+
### Environment Variables (Lambda)
|
|
232
|
+
|
|
233
|
+
- `TRIPLES_TABLE` - DynamoDB table name (auto-set by CDK)
|
|
234
|
+
- `ENVIRONMENT` - Deployment environment (dev, staging, prod)
|
|
235
|
+
- `NODE_ENV` - Node environment (production)
|
|
236
|
+
- `BATCH_SIZE` - Batch size for bulk operations (default: 100)
|
|
237
|
+
|
|
238
|
+
### CDK Stack Options
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
interface UNRDFStackConfig {
|
|
242
|
+
environment: 'dev' | 'staging' | 'prod';
|
|
243
|
+
memorySizeMb: number; // 128-10240
|
|
244
|
+
timeoutSeconds: number; // 3-900
|
|
245
|
+
enableCdn: boolean; // CloudFront
|
|
246
|
+
enableStreaming: boolean; // DynamoDB streams
|
|
247
|
+
tableName?: string; // Custom table name
|
|
248
|
+
apiName?: string; // Custom API name
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Performance
|
|
253
|
+
|
|
254
|
+
### Cold Start Optimization
|
|
255
|
+
|
|
256
|
+
- **Minimal bundles** - Tree-shaking reduces size by 70%+
|
|
257
|
+
- **No AWS SDK** - Excluded from bundle (provided by Lambda)
|
|
258
|
+
- **ES modules** - Faster parsing than CommonJS
|
|
259
|
+
- **Provisioned concurrency** - Optional for production
|
|
260
|
+
|
|
261
|
+
### Query Optimization
|
|
262
|
+
|
|
263
|
+
- **GSI indexes** - Sub-100ms predicate/object queries
|
|
264
|
+
- **Batch operations** - 25 items per DynamoDB batch
|
|
265
|
+
- **Pagination** - Cursor-based for large results
|
|
266
|
+
- **Caching** - CloudFront edge caching (optional)
|
|
267
|
+
|
|
268
|
+
### Cost Optimization
|
|
269
|
+
|
|
270
|
+
- **Pay-per-request** - DynamoDB on-demand billing
|
|
271
|
+
- **Auto-scaling Lambda** - No idle costs
|
|
272
|
+
- **Minimal memory** - Right-sized allocations
|
|
273
|
+
- **CDN caching** - Reduced Lambda invocations
|
|
274
|
+
|
|
275
|
+
## Deployment Best Practices
|
|
276
|
+
|
|
277
|
+
1. **Start with dry-run** - Validate configuration before deploying
|
|
278
|
+
2. **Use environments** - Separate dev/staging/prod stacks
|
|
279
|
+
3. **Enable CDN for production** - Global performance
|
|
280
|
+
4. **Monitor with X-Ray** - Built-in tracing
|
|
281
|
+
5. **Set concurrency limits** - Prevent runaway costs
|
|
282
|
+
6. **Enable point-in-time recovery** - Production data safety
|
|
283
|
+
|
|
284
|
+
## Testing
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
# Run tests
|
|
288
|
+
pnpm test
|
|
289
|
+
|
|
290
|
+
# With coverage
|
|
291
|
+
pnpm test:coverage
|
|
292
|
+
|
|
293
|
+
# Watch mode
|
|
294
|
+
pnpm test:watch
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Documentation
|
|
298
|
+
|
|
299
|
+
- [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/)
|
|
300
|
+
- [Lambda Best Practices](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html)
|
|
301
|
+
- [DynamoDB Design Patterns](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unrdf/serverless",
|
|
3
|
+
"version": "26.4.2",
|
|
4
|
+
"description": "UNRDF Serverless - One-click AWS deployment for RDF applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.mjs",
|
|
9
|
+
"./cdk": "./src/cdk/index.mjs",
|
|
10
|
+
"./deploy": "./src/deploy/index.mjs",
|
|
11
|
+
"./api": "./src/api/index.mjs",
|
|
12
|
+
"./storage": "./src/storage/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"dist/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "vitest run --no-coverage",
|
|
23
|
+
"test:coverage": "vitest run --coverage",
|
|
24
|
+
"test:fast": "vitest run --no-coverage",
|
|
25
|
+
"test:watch": "vitest --no-coverage",
|
|
26
|
+
"build": "node build.config.mjs",
|
|
27
|
+
"lint": "eslint src/ test/ --max-warnings=0",
|
|
28
|
+
"lint:fix": "eslint src/ test/ --fix",
|
|
29
|
+
"format": "prettier --write src/",
|
|
30
|
+
"format:check": "prettier --check src/",
|
|
31
|
+
"clean": "rm -rf dist/ .nyc_output/ coverage/ cdk.out/",
|
|
32
|
+
"deploy:demo": "node examples/deploy-demo.mjs",
|
|
33
|
+
"cdk:synth": "cdk synth",
|
|
34
|
+
"cdk:deploy": "cdk deploy",
|
|
35
|
+
"cdk:destroy": "cdk destroy"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"rdf",
|
|
39
|
+
"serverless",
|
|
40
|
+
"aws",
|
|
41
|
+
"lambda",
|
|
42
|
+
"cdk",
|
|
43
|
+
"infrastructure",
|
|
44
|
+
"deployment"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@unrdf/core": "workspace:*",
|
|
48
|
+
"@unrdf/oxigraph": "workspace:*",
|
|
49
|
+
"aws-cdk-lib": "^2.165.0",
|
|
50
|
+
"constructs": "^10.4.2",
|
|
51
|
+
"esbuild": "^0.24.2",
|
|
52
|
+
"zod": "^4.1.13"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@aws-sdk/client-dynamodb": "^3.712.0",
|
|
56
|
+
"@aws-sdk/client-lambda": "^3.712.0",
|
|
57
|
+
"@aws-sdk/lib-dynamodb": "^3.712.0",
|
|
58
|
+
"@types/node": "^24.10.1",
|
|
59
|
+
"vitest": "^4.0.15"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@aws-sdk/client-dynamodb": "^3.x",
|
|
63
|
+
"@aws-sdk/client-lambda": "^3.x"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
67
|
+
},
|
|
68
|
+
"repository": {
|
|
69
|
+
"type": "git",
|
|
70
|
+
"url": "https://github.com/unrdf/unrdf.git",
|
|
71
|
+
"directory": "packages/serverless"
|
|
72
|
+
},
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/unrdf/unrdf/issues"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://github.com/unrdf/unrdf#readme",
|
|
77
|
+
"license": "MIT",
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"access": "public"
|
|
80
|
+
}
|
|
81
|
+
}
|