ð Migration Guide: acai-js to acai-ts
Acai-TS is a complete TypeScript rewrite of acai-js with improved type safety, decorator support, and modern ES6+ features. This guide will help you migrate your existing acai-js code to acai-ts.
Quick Start
The main changes are:
- Install
acai-ts
and reflect-metadata
- Convert JavaScript to TypeScript
- Update imports to use ES6 modules
- Add type annotations
- Use decorators for cleaner endpoint definitions (optional)
ðĶ Installation Changes
Before (acai-js)
After (acai-ts)
| npm install acai-ts reflect-metadata
|
New Requirements:
- Node.js >= 18.18.2
- TypeScript >= 5.0
- reflect-metadata
package (for decorator support)
ð Import Changes
Before (acai-js)
| const { Router, Event } = require('acai-js');
|
After (acai-ts)
| import 'reflect-metadata';
import { Router, Event } from 'acai-ts';
import { APIGatewayProxyEvent, DynamoDBStreamEvent } from 'aws-lambda';
|
ð§ TypeScript Configuration
Add to your tsconfig.json
:
| {
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true
}
}
|
ð APIGateway Router Changes
Before (acai-js)
| const { Router } = require('acai-js');
exports.handler = async (event) => {
const router = new Router({
basePath: '/api/v1',
schemaPath: './openapi.yml'
});
return await router.route(event);
};
|
After (acai-ts)
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | import 'reflect-metadata';
import { Router } from 'acai-ts';
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
const router = new Router({
basePath: '/api/v1',
schemaPath: './openapi.yml'
});
return await router.route(event);
};
|
ðŊ Endpoint Changes
Pattern-Based (File-Based) Routing
Before (acai-js)
| // users.js
exports.requirements = {
post: {
requiredBody: 'CreateUserRequest'
}
};
exports.post = async (request, response) => {
response.body = { id: '123', ...request.body };
return response;
};
|
After (acai-ts)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | // users.ts
import { Request, Response } from 'acai-ts';
export const requirements = {
post: {
requiredBody: 'CreateUserRequest'
}
};
export const post = async (
request: Request,
response: Response
): Promise<Response> => {
response.body = { id: '123', ...request.body };
return response;
};
|
ðĻ Decorator-Based Routing (NEW in acai-ts!)
| // File: src/handlers/users.ts
import { BaseEndpoint, Validate, Response, Request } from 'acai-ts';
export class UsersEndpoint extends BaseEndpoint {
@Validate({ requiredBody: 'CreateUserRequest' })
async post(request: Request, response: Response): Promise<Response> {
response.body = { id: '123', ...request.body };
return response;
}
}
|
ðïļ Event Handler Changes
ð DynamoDB Streams
Before (acai-js)
| const { Event } = require('acai-js');
exports.handler = async (event) => {
const dynamodb = new Event(event, {
operations: ['INSERT', 'MODIFY']
});
for (const record of dynamodb.records) {
console.log(record.newImage);
}
};
|
After (acai-ts)
1
2
3
4
5
6
7
8
9
10
11
12 | import { Event } from 'acai-ts';
import { DynamoDBStreamEvent } from 'aws-lambda';
export const handler = async (event: DynamoDBStreamEvent): Promise<void> => {
const dynamodb = new Event(event, {
operations: ['INSERT', 'MODIFY']
});
for (const record of dynamodb.records) {
console.log(record.newImage);
}
};
|
ðĻĢ S3 Events
Before (acai-js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | const { Event } = require('acai-js');
exports.handler = async (event) => {
const s3Event = new Event(event, {
getObject: true,
isJSON: true
});
await s3Event.process();
for (const record of s3Event.records) {
console.log(record.body);
}
};
|
After (acai-ts)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | import { Event } from 'acai-ts';
import { S3Event } from 'aws-lambda';
export const handler = async (event: S3Event): Promise<void> => {
const s3Event = new Event(event, {
getObject: true,
isJSON: true
});
await s3Event.process();
for (const record of s3Event.records) {
console.log(record.body);
}
};
|
ðŽ SQS Messages
Before (acai-js)
| const { Event } = require('acai-js');
exports.handler = async (event) => {
const sqsEvent = new Event(event);
for (const record of sqsEvent.records) {
console.log(record.body);
}
};
|
After (acai-ts)
| import { Event } from 'acai-ts';
import { SQSEvent } from 'aws-lambda';
export const handler = async (event: SQSEvent): Promise<void> => {
const sqsEvent = new Event(event);
for (const record of sqsEvent.records) {
console.log(record.body);
}
};
|
ð Logger Changes
Before (acai-js)
| global.logger.info('message');
global.logger.error('error');
|
After (acai-ts)
| import { Logger } from 'acai-ts';
Logger.info('message');
Logger.error('error');
|
âĻ New Features in acai-ts
1ïļâĢ Decorator Support
Use decorators for cleaner endpoint definitions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | // File: src/handlers/users.ts
import { BaseEndpoint, Validate, Before, After, Timeout, Request, Response } from 'acai-ts';
const authMiddleware = async (request: Request, response: Response) => {
// Auth logic
};
const loggingMiddleware = async (request: Request, response: Response) => {
// Logging logic
};
export class UsersEndpoint extends BaseEndpoint {
@Validate({ requiredBody: 'CreateUserRequest' })
@Before(authMiddleware)
@After(loggingMiddleware)
@Timeout(30000)
async post(request: Request, response: Response): Promise<Response> {
// Clean business logic
response.body = { id: '123', ...request.body };
return response;
}
}
|
2ïļâĢ Full TypeScript Type Safety
All classes, interfaces, and functions are fully typed:
| import { Request, Response } from 'acai-ts';
const request: Request = {
path: '/users',
method: 'POST',
body: { email: 'user@example.com' },
headers: {},
queryParameters: {},
pathParameters: {}
};
|
3ïļâĢ Custom Data Classes with Types
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | interface UserData {
id: string;
email: string;
}
class User {
id: string;
email: string;
constructor(record: any) {
this.id = record.body.id;
this.email = record.body.email;
}
sendWelcomeEmail(): void {
// Type-safe method
}
}
const dynamodb = new Event<User>(event, {
dataClass: User,
operations: ['INSERT']
});
for (const user of dynamodb.records) {
user.sendWelcomeEmail(); // Fully typed!
}
|
4ïļâĢ Improved Error Handling
| import { ApiError } from 'acai-ts';
throw new ApiError('User not found', 404, 'user_id');
|
ð API Compatibility
Most APIs remain compatible, but with added type safety:
acai-js |
acai-ts |
Notes |
Router |
Router |
Same API, now typed |
Event |
Event |
Same API, now typed |
Request |
RequestClient / Request |
Typed interface |
Response |
ResponseClient / Response |
Typed interface |
global.logger |
Logger |
Import from package |
â ïļ Breaking Changes
1. Package Name
- Old:
acai-js
or @syngenta-digital/Acai
- New:
acai-ts
2. Node.js Version
- Old: Node 10.10+
- New: Node 18.18.2+
3. File Extensions
- Old:
.js
files
- New:
.ts
files
4. Export Syntax
- Old:
exports.handler
- New:
export const handler
5. Import Syntax
- Old:
require()
- New:
import
statements
â
Migration Checklist
- [ ] Install
acai-ts
and reflect-metadata
- [ ] Update
tsconfig.json
with decorator support
- [ ] Convert
.js
files to .ts
- [ ] Change
require()
to import
statements
- [ ] Change
exports.
to export const
- [ ] Add AWS Lambda event types
- [ ] Add type annotations to functions
- [ ] Update
acai-js
imports to acai-ts
- [ ] Add
import 'reflect-metadata'
at entry points
- [ ] Test all endpoints and event handlers
- [ ] Update deployment configuration for TypeScript build
ð Need Help?