raaja
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

Yantra

Serverless tooling built from existing AIS tooling:

  • AIS Service Discovery
  • Jedlik

Rationale

Currently, we lean heavily on the serverless framework, which is a massive project, and abstracts away some of the need to write Cloudformation. We only use a small sub-set of Serverless's functionality. I.e. registering lambdas with API Gateways etc. This project aims to take some of the functionality we get from Serverless, and replace it work AWS's CDK, and combine it with some of our additional tooling. In order to create an end to end framework for creating serverless apps. With the aim of writing as little cloudformation and in fact yaml as possible.

Leaning more on AWS CDK, means we remove the time spent trying to guess the correct permissions and security rules, as AWS CDK manages the security relationships between your infrastructure components automatically, and to the most optimally secure path.

This would also give us more control over spinning up services and fine-tuning them to our requirements. Because this is just code, just Javascript, we can write plugins, libraries and abstractions, to reduce the amount of configuration and boiler-plate we write.

This is experimental/10% time stuff to validate the idea, so probably don't try to use it for anything yet...

One final key aim is to be able to create a modular environment whereby we can run and test our business logic in multiple configurations. We aim achieve this by transposing the arguments from the various environments, and the return values from business logic, into the same sets of patterns. For example, if I have some business logic, which takes a name and an email as arguments, it should be seemless to switch between a local server, and the Lambda environment for example.

We include a 'transpositions' package, which will extract the required fields from a Lambda event, and pass them into a usecase.

Aims

  • Unified way of creating services, with a degree of flexibility.
  • Modular approach to configuring and extending services.
  • One correct way to do each common task, which can be optimised over time.
  • Cutting time spent on boilerplate code and repeatable set-up, utilising code as infrastructure and abstractions.
  • Infrastructure and application code in the same language, seamless experience between the two.
  • Ability to run locally out of the box.
  • Support for HTTP and GraphQL.
  • Seamless communication using Cloudmap + AIS Service Discovery as its backbone.
  • Less fucking YAML.
  • Telemetry, tracing and tooling out of the box.
  • Mind blowing developer experience 🤯.

Note: on AppSync - We can actually hook up resolvers and mapping templates etc for AppSycn using the CDK. Which begs the question, to solve the problem we have currently with out AppSync set-up, could we solve that with this: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-appsync-readme.html

Auto-transpose usecases into deliveries

const container = require('./lib');

container.registerUsecase('myUsecase', require('./usecases/my-lambda-handler'));

const handler = container.resolveAsLambda('myUsecase', ['name', 'email']);

Integration with Service Discovery

const container = require('./lib');

container.registerService('myQueue', ServiceTypes.QUEUE, 'latest-namespace.srv.queue');

const handler = ({ myQueue }) => message => {
  const { MessageId } = myQueue.queue(message);
  return {
    MessageId,
  };
};


container.registerUsecase('myUsecase', handler);

module.exports = container.resolveAsLambda('myUsecase');

Declerative Infrastructure

Example registering Lambdas to an API Gateway...

class MyApp extends App {

    constructor(opts: Opts) {
        super(opts);
        this.createMessage();
        this.getMessage();
    }

    @http("/api/create-message", ["POST"])
    createMessage() {
        return this.registerHandler('create-message', './handlers');
    }

    @http("/api/get-messages", ["GET"])
    getMessage() {
        return this.registerHandler('get-message', './handlers');
    }

    @subscribe([
        "ais-latest.tenant.onTenantCreated",
        "ais-latest.tenant.onNewUser",
    ])
    sendEmail() {
        return this.registerHandler('send-email', 'send-email/index.handler');
    }

    @listen([
        'ais-latest.feeds.stream',
    ])
    processFeed() {
        return this.registerHandler('process-feed/index.handler');
    }
}

const app = new MyApp({
    type: 'http',
    service: 'my-app',
    stage: 'latest',
    resource: 'messages',
    server: new HttpServer(), // For local use
});
app.deploy(); // Deploys with AWS CDK CLI
app.run(); // Runs locally

Example resource class

The following generates a DynamoDB table from the given Jedlik Entity. If then creates an API Gateway resource, and registers each handler to the correct method.

import {App, Opts} from 'yantra';
import {Feed} from './entities/feed';

@Entity(Feed)
@Resource("feeds")
class MyApp extends App {
    constructor(opts: Opts) {
        super(opts);
    }
    
    @get
    getFeed() {
        this.registerHandler('./handlers/feeds/get');
    }
    
    @post
    createFeed() {
        this.registerHandler('./handlers/feeds/create');
    }
    
    @put
    updateFeed() {
        this.registerHandler('./handlers/feeds/update');
    }
}

To run the above:

$ aws cdk

Example GraphQL (AppSync)

@GraphQL("./graphql")
class MyApp extends App {
    constructor(opts: Opts) {
        super(opts);
    }
    
    @resolver('createUser')
    createUserResolver() {
        this.registerResolver('./resolvers/create-user');
    }
}

const app = new MyApp({
    service: 'my-graphql-service',
    type: types.GraphQL,
    server: new GraphQLServer(),
});

Readme

Keywords

none

Package Sidebar

Install

npm i raaja

Weekly Downloads

1

Version

1.0.0

License

MIT

Unpacked Size

524 kB

Total Files

59

Last publish

Collaborators

  • peakai