typescript decorators for api development
Annotate a class with @Endpoint its methods with @Lambda with metadata that can be retrieved at runtime to bind them to providers such as AWS Lambda, Express.js, Google Functions, Azure Function and potentially any.
> Right now the only providers available are: __AWS__ with their Lambda service and __Express.js__ see use with express.js
> A serverless plugin is provided. It uses the metadata within the classes to __create at runtime__ the serverless configuration. In other words the developer won't need to worry about adding/editing serverless.yml functions section. See serverless-apigator
yarn add @microgamma/apigator
`Create the app container
`typescript`Create a service file.
`typescript
// my-first.service.ts
import { Endpoint, Lambda } from '@microgamma/apigator';@Endpoint({
cors: true,
name: 'my-first-service',
private: false
})
export class MyFirstService {
@Lambda({
method: 'GET',
path: '/'
})
public index() {
return
Hello world! Today is ${new Date().toISOString()};
}
}
`
Now you can retrieve the metadata at run time:`typescript
import { getEndpointMetadataFromClass, getLambdaMetadataFromClass } from '@microgamma/apigator';
import { MyFirstService } from './my-first.service';const endpointMetadata = getEndpointMetadataFromClass(MyFirstService);
console.log({endpointMetadata});
const lambdas = getLambdaMetadataFromClass(MyFirstService);
lambdas.forEach((lambda) => console.log({lambda}));
/*
{
endpointMetadata: { cors: true, name: 'my-first-service', private: false }
}
{
lambda: { method: 'GET', path: '/', integration: 'lambda', name: 'index' }
}
*/
`
Usually you won't need to do the above though.$3
The @Lambda decorator stores the provided metadata and wraps the annotated method inside an asynchronous function. At runtime the function's real arguments may vary: i.e.: if it's called as an Aws Lambda its arguments will be event, context whether if called within express they will be request, response. To handle the differences between environments we use a service called LambdaDefaultHandler. By default, the AwsEventHandler is used. To use a different handler it must be specified in the @Endpoint decorator (see below).Valid values are:
`typescript
interface LambdaOptions {
name?: string; // name of the function, MUST not be set!
path: string; // the sub path which the lambda will be available.
method: string; // http method. can be any of the http verbs
integration?: string; // only when using serverless: the integration to use. This is equivalent to set the integration into serverless.yml (Default: 'lambda')
private?: boolean; // only when using serverless: equivalent to set private in serverless.yml (Default: false)
cors?: boolean; // only when using serverless: equivalent to set cors in serverless.yml defaults to false
authorizer?: string | {}; // only when using serverless: equivalent to providing an authorizer function in serverless.yml
description?: string;
}
`$3
The endpoint decorator stores the provided metadata and sets the implementation of the LambdaDefaultHandler to use.
Valid values are:
`typescript
interface EndpointOptions {
readonly name: string; // name of the endpoint
readonly basePath?: string; // base path that will be prepended to the path defined in each lambda.
readonly private?: boolean; // Default: false. Setting this field is equivalent to add its value to every @Lambda. If any @Lambda as private set then that will have precedence
readonly cors?: boolean; // Default: false. Same as private.
}
`
I.e. when using it with express.js ExpressEventHandler needs to be used.
`typescript@Endpoint({
cors: true,
name: 'my-first-service',
private: false,
providers: [{
provide: LambdaDefaultHandler,
implementation: ExpressEventHandler
}]
})
`
$3
Retrieves a value from the path and assign it to the annotated argument
`typescript
@Lambda({
method: 'GET',
path: '/me/{name}'
})
public me(@Path('name') user) {
return Hello ${user};
}
`$3
Retrieves the body and assign it to the annotated argument
`typescript
@Lambda({
method: 'POST',
path: '/me'
})
public me(@Body() user) {
return Hello ${user};
}
`$3
Retrieves a value from the http headers and assign it to the annotated argument
`typescript
@Lambda({
method: 'GET',
path: '/me'
})
public me(@Header('name') user) {
return Hello ${user};
}
`Use with express.js
Install express.js
`
yarn add express @types/express
`
Set ExpressEventHanlder as described above.Be aware that express uses the
:param notation for path parameters so the lambda path need to be such as`typescript
@Lambda({
method: 'GET',
path: '/me/:name'
})
public me(@Path('name') user) {
return Hello ${user};
}
`Create
server.ts file:`typescript
import { bootstrap, getEndpointMetadataFromClass, getLambdaMetadataFromClass, LambdaOptions } from '@microgamma/apigator';
import express, { Application } from 'express';
import { MyFirstService } from './my-first.service';const app: Application = express();
const port = 3000;
const service = bootstrap(MyFirstService);
function createExpressHandler(lambda: LambdaOptions, app: Express.Application) {
applambda.method.toLowerCase();
}
lambdas.forEach((lambda) => createExpressHandler(lambda, app));
app.listen(port, () => console.log(
Example app listening on port ${port}!));`
Run after transpile or with ts-node`bash
yarn ts-node server.ts
`
You can hit your lambda at localhost:3000`See serverless-apigator for more information.