the transpiler for escaping from complicated usage
of cloud services and APIs
Table of Contents
- Prerequisites
- Installation
- Usage
- Configuration
- Transpilation features
- Publications
- License
- 日本語ドキュメント
Prerequisites
- Node.js 10.x or later
- Serverless Framework
Installation
npm install --save-dev escapin
Usage
Escapin provides CLI escapin
that works on Node.js project directories containing ./package.json
.
First, append the following scripts in package.json
:
Then, run build
and start
on the project folder:
npm run buildnpm start
Escapin transpiles your source code into executable one as a serverless application, and generates serverless.yml
that can be used for deploying the programs to cloud services by Serverless Framework.
CLI options
Usage: escapin [options]
Options:
-V, --version output the version number
-d, --dir <dir> working directory (default: ".")
--ignore-path <path> specify path of ignore file (default: ".gitignore")
-h, --help output usage information
Configuration
You can give configuration information to Escapin CLI by using the following ways:
Place | Format |
---|---|
escapin property in package.json |
JSON |
.escapinrc |
JSON or YAML |
.escapinrc.json |
JSON |
.escapinrc.yaml or .escapinrc.yml |
YAML |
.escapinrc.js or escapin.config.js |
JavaScript |
Here is the example of JSON configuration file .escapinrc
.
moduleexports = name: "sendmail" api_spec: "swagger.yaml" credentials: api: "mailgun API" basicAuth: "api:<YOUR_API_KEY>" platform: "aws" default_storage: "table" output_dir: "build" http_client: "axios";
Name | Description | Options | Default |
---|---|---|---|
name |
name of the application | ||
api_spec |
path of the specification file of the API published by the application | ||
credentials |
credentials required in calling external APIs | ||
platform |
cloud platform where the application is being deployed | aws |
aws |
default_storage |
the storage type that are selected by default | table bucket |
table |
output_dir |
directory where the transpilcation artifacts are being stored | build |
|
http_client |
http client used in generated code for requesting apis defined by OAS | axios request |
axios |
Transpilation features
Storage
You can use several kinds of storage services just like a first-class object in JavaScript.
By declaring an empty object placing a special type annotation (e.g., bucket
) you can create a resource in that type of storage services.
You can use both canonical type platform.storageType
(e.g., aws.bucket
) and shorthand type storageType
(e.g., bucket
) for storage objects; platform
in the configuration file is used in shorthand types.
If you omit a type annotation, default_storage
is used as that type by default.
In v0.2.x, bucket
and table
is available for storage types; bucket
represents a bucket in object storage, and table
represents a table in NoSQL datastore service.
const foo: aws.bucket = {}; // AWS S3 Bucketconst bar: bucket = {}; // AWS S3 Bucketconst baz: table = {}; // AWS DynamoDB Tableconst qux = {}; // AWS DyanmoDB Table
Here are the usage example of storage objects:
const foo: bucket = {}; fooid = bar; // uploading databaz = fooid; // downloading dataqux = Object; // obtaining keys of datadelete fooid; // deleting existing data
Input
index.js
const foo: table = {}; fooid = bar;
.escapinrc.js
moduleexports = platform: "aws" ...;
Output
index.js
; await { ;};
serverless.yml
resources: Resources: fooTable: Type: AWS::DynamoDB::Table Properties: TableName: foo-9fe932f9-32e7-49f7-a341-0dca29a8bb32 KeySchema: - AttributeName: key KeyType: HASH AttributeDefinitions: - AttributeName: key AttributeType: S ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 escapinFunctionRole: Properties: Policies: - PolicyName: foo-9fe932f9-32e7-49f7-a341-0dca29a8bb32-FullAccess PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "dynamodb:ListGlobalTables" - "dynamodb:ListTables" Resource: "*" - Effect: Allow Action: "dynamodb:*" Resource: "Fn::GetAtt": - fooTable - Arn
Function
Input
index.js
{ if throw "[400] An error occured"; return message: "Succeeded" ;}
.escapinrc.js
moduleexports = name: "myapp" platform: "aws" api_spec: "swagger.yaml" ...;
swagger.yaml
swagger: "2.0"info: version: "1.0.0" title: "myapp"host: "myapp.org"basePath: "/v1"schemes: - "http"produces: - "application/json"paths: /handle: get: summary: "handler" x-escapin-handler: "index.handler" parameters: [] responses: 200: schema: $ref: "#/definitions/Message" 400: schema: $ref: "#/definitions/Error" ...
Output
index.js
{ if ; return; ; return;}
serverless.yml
functions: handlerFunction: handler: index.handler runtime: nodejs10.x role: escapinFunctionRole events: - http: path: handle method: get cors: true integration: lambdaresources: Resources: escapinFunctionRole: Type: "AWS::IAM::Role" Properties: Path: /escapin/ RoleName: myappEscapinFunctionRole AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: "sts:AssumeRole" Policies: ...
Importing open APIs
Usage
;
Method | Path | Header | Body | Example |
---|---|---|---|---|
GET |
/items |
items = api.items; |
||
GET |
/items/:id |
item = api.items[id]; |
||
GET |
/items/:id/props |
props = api.items[id].props; |
||
GET |
/items/:id?foo=bar |
item = api.items[id] [ { foo: 'bar' } ] ; |
||
GET |
/items/:id?foo=bar |
baz: qux |
item = api.items[id] [ { foo: 'bar', baz: 'qux' } ] ; |
|
POST |
/:domain/messages |
{ quux: 'corge' } |
api. domain[domain].messages ( { quux: 'corge' } ) ; |
|
POST |
/items |
{ quux: 'corge' } |
api.items ( { quux: 'corge' } ) ; |
|
POST |
/items/:id?foo=bar |
baz: qux |
{ quux: 'corge' } |
api.items[id] [ { foo: 'bar', baz: 'qux' } ] ( { quux: 'corge' } ) ; |
PUT |
/items/:id |
baz: qux |
{ quux: 'corge' } |
api.items[id] [ { baz: 'qux' } ] = { quux: 'corge' }; |
DELETE |
/items/:id |
delete api.items[id]; |
Input
index.js
;apiitemsid foo: "bar" baz: "qux" quux: "corge" ;
.escapinrc.js
moduleexports = http_client: "request" ...;
http://path/to/swagger.yaml
swagger: "2.0"info: title: Awesome API description: An awesome API version: "1.0.0"host: "api.endpoint.com"schemes: - httpbasePath: /v1produces: - application/jsonconsumes: - application/jsonpaths: /items/{id}: post: description: Do some task regarding an item parameters: - name: id in: path type: string required: true description: Item ID - name: foo in: query type: string required: true - name: baz in: header type: string required: true - name: params in: body schema: $ref: "#/definitions/Params" responses: "200": description: Succeeded schema: $ref: "#/definitions/Message"definitions: Params: type: object properties: quux: type: string Message: type: object properties: message: type: string
Output
index.js
;const _res _body = ;
Publishing your API
Input
index.js
{ const id = reqpathid; const foo = reqqueryfoo; const baz = reqheaderbaz; const quux = reqbodyquux; if throw "[400] An error occured."; return message: "Succeeded" ;}
swagger.yaml
swagger: "2.0"info: title: Awesome API description: An awesome API version: "1.0.0"host: "api.endpoint.com"schemes: - httpsbasePath: /v1produces: - application/jsonconsumes: - application/jsonpaths: /items/{id}: post: x-escapin-handler: index.handleItem description: Do some task regarding an item parameters: - name: id in: path type: string required: true description: Item ID - name: foo in: query type: string required: true - name: baz in: header type: string required: true - name: params in: body schema: $ref: "#/definitions/Params" responses: "200": schema: $ref: "#/definitions/Message" "400": schema: $ref: "#/definitions/Error"definitions: Params: type: object properties: quux: type: string
Output
index.js
{ const id = reqpathid; const foo = reqqueryfoo; const baz = reqheaderbaz; const quux = reqbodyquux; if ; return; ; return;}
serverless.yml
functions: handleItemFunction: handler: index.handleItem runtime: nodejs10.x role: escapinFunctionRole events: - http: path: 'items/{id}' method: post cors: true integration: lambda ...
Auto-completing asynchronous features
Destructuring nesting callbacks
Original
{ ;}
Destructured
{ try const data1 data2 = ; ; catch err ; }
Asynchronized
{ try const _data = await { ; }; ; catch err ; }
for, for-in, for-of (collection should be obtained asynchronously)
Input
for const item of api ;
Output
const _data = await { api;};for const item of _data ;
for, for-in, for-of (executable in parallel)
Input
for const arg of args api;
Output
const _promises = ;for const arg of args _promises;await Promiseall_promises;
for, for-in, for-of (NOT executable in parallel)
Input
let sum = 0;for const arg of args sum += api;
Output
let sum = 0;for const arg of args const _data = await { api; }; sum += _data;
while,do-while
Input
while data = api === null ;
Output
let _data = await { api;};while data = _data === null ; _data = await { api; };
if-else
Input
if api ; else if api ;
Output
const _data = await { api;};if _data ; else let _data2 = await { api; }; if _data2 ;
switch-case
Input
Output
let _promise;const _data = await { api;};
functions that require callback functions as an argument (e.g., Array#forEach)
Input
// special rules are applied for Array#map and Array#forEachargs;args; args;
Output
; await Promiseallargs;args; args;
asynchronous features appearing in input
Input
args;
Output
await Promiseallargs;
Publications
-
Kosaku Kimura, Atsuji Sekiguchi, Shridhar Choudhary and Tadahiro Uehara, "A JavaScript Transpiler for Escaping from Complicated Usage of Cloud Services and APIs," 2018 25th Asia-Pacific Software Engineering Conference (APSEC), Nara, Japan, 2018, pp. 69-78.
-
An Introduction of a Technology for Simplifying Serverless Application Programming in AWS with Node.js, JAWS DAYS 2019
-
FaaS 上のコードをもっとシンプルに書くためのトランスパイラ, Serverless Meetup Tokyo #13