Skip to content

lynx-r/aws-lambda-cognito-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Example of implementation API Amazon Cognito

The project structure

.
├── README.md
├── bin
│   └── www // enterpoint for the local server
├── package.json
├── serverless.yml // a configuration of Serverless Framework 
├── src
│   ├── config
│   │   ├── config.json // a configuration of the local project
│   │   └── config.ts
│   ├── constant
│   │   ├── app-constants.ts
│   │   ├── tags.ts
│   │   └── types.ts
│   ├── controller // controllers based on inversify-restify-utils 
│   │   ├── article.controller.ts
│   │   ├── home.controller.ts
│   │   └── user.controller.ts
│   ├── http
│   │   └── response.ts
│   ├── ioc // inversify
│   │   ├── ioc.ts
│   │   └── loader.ts
│   ├── lambda.ts // enterpoint for AWS Lambda
│   ├── server-base.ts // the base class for the server. base settings
│   ├── server-lambda.ts // inherited from base, class for lambda 
│   ├── server-local.ts // inherited from base, class for local
│   └── service
│       ├── article.service.ts
│       └── user.service.ts
└── tsconfig.json

We are going to compile the project in JavaScript with help of IntelliJIdea.

For that purpose edit tsconfig.js by setting compileOnSave to true

  "compileOnSave": true, // required for onfly compilation
  "compilerOptions": {
    "outDir": "./dist",
    "baseUrl": "src",
    "sourceMap": true,
    "inlineSources": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "noImplicitAny": false,
    "target": "es6",
    "typeRoots": [
      "node_modules/@types"
    ],
    "types": [
      "reflect-metadata"
    ],
    "module": "commonjs",
    "lib": [
      "es2017",
      "dom"
    ]
  }
}

Enable TypeScript compiler in the settings:

Compiler's settings

Add task Compile TypeScript before launching:

Compile TypeScript

Create watcher for config.json. It watches for changes and copies that file to dist/config File watcher

All previous tasks can be done by using grunt or gulp

Configure Inversion of Control. By creating ioc.ts:

import 'reflect-metadata'; // don't forget to import this 
import {Container, inject} from 'inversify';
import {autoProvide, makeProvideDecorator, makeFluentProvideDecorator} from 'inversify-binding-decorators';
import {makeLoggerMiddleware} from 'inversify-logger-middleware';

let container = new Container();

if (process.env.NODE_ENV === 'development') { // for logging of injections
  // let logger = makeLoggerMiddleware();
  // container.applyMiddleware(logger);
}

let provide = makeProvideDecorator(container);
let fluentProvider = makeFluentProvideDecorator(container);

let provideSingleton = function(identifier) { // the annotation for providing singleton
  return fluentProvider(identifier)
    .inSingletonScope()
    .done();
};

let provideNamed = function (identifier, name) { // the annotation for providing by name
  return fluentProvider(identifier)
    .inSingletonScope()
    .whenTargetNamed(name)
    .done();
};

let bindDependencies = function (func, dependencies) { // for a function injections. e.g. func = bindDependencies(myFunc, [TYPES.MyService]); func();
  let injections = dependencies.map((dependency) => {
    return container.get(dependency);
  });
  return func.bind(func, ...injections);
};

export {container, autoProvide, provide, provideNamed, provideSingleton,
  inject, bindDependencies};

Import all dependencies in loader.ts:

import '../controller/home.controller';
import '../controller/user.controller';
import '../controller/article.controller';
import '../service/user.service';
import '../service/article.service';

Let's configure server

Configuration:

config(app) {
  // configure cors
  app.use(restify.CORS({
    origins: nconf.get("server:origins"),   // defaults to ['*']
    credentials: false,                 // defaults to false
  }));
  
  // to get query params in req.query
  app.use(restify.acceptParser(app.acceptable));
  // to get passed json in req.body
  app.use(restify.bodyParser());
  
  // error handler
  app.on('error', (error) => {
    this.onError(error);
  });
  // process exceptions
  app.on('uncaughtException', function (request, response, route, error) {
    console.error(error.stack);
    response.send(error);
  });
  // audit logger
  app.on('after', restify.auditLogger({
    log: this.logger
  }));
  
  app.use(helmet()); // прячем некоторые заголовки вроде X-Powered-By
}

Place the server in the inversify container:

bootstrap(): restify.Server {
  super.bootstrap();
  //create restify application
  this.app = new InversifyRestifyServer(container, {
    name: AppConstants.APP_NAME,
    version: nconf.get("server:api_version"),
    log: this.logger
  }).setConfig((app) => {
    this.config(app);
    this.listen(app);
  }).build();
}

Let's create the UserController controller:

@Controller('/users') // annotation from inversify-restify-utils
@provideNamed(TYPE.Controller, TAGS.UserController) // inject by name
export class UserController implements interfaces.Controller {

  constructor(@inject(TYPES.UserService) private userService: UserService) { // injecting services
  }

  @Post('/register') // annotate method as the post request
  register(req: restify.Request, res: restify.Response, next: restify.Next) {
    this.userService.register(req.body.given_name, req.body.email, req.body.password, (err, user) => {
      if (err) {
        console.error(err.message);
        return res.json(new Response(false, err));
      }
      res.json(new Response(true, 'User was created', user));
      next();
    });
  }
}

Let's create the UserService:

@provideSingleton(TYPES.UserService) // inject as singleton 
export class UserService {

  constructor() {
  }

  register(given_name: string, email: string, password: string,
           callback: (error, response) => any) {
    const params = {
      ClientId: nconf.get('aws:cognito:user_pool_client_id'),
      Username: email,
      Password: password,
      UserAttributes: [
        {Name: "email", Value: email},
        {Name: "given_name", Value: given_name}
      ]
    };
    console.log(params);

    const cognitoidentityserviceprovider = new CognitoIdentityServiceProvider(UserService.getAWSRegion());
    cognitoidentityserviceprovider.signUp(params, callback);
  }
}

Let's look into the working with DynamoDB

Create table:

const AWS = require("aws-sdk");

AWS.config.update({
  region: "us-west-2",
  endpoint: "http://localhost:8000"
});

const dynamodb = new AWS.DynamoDB();

let params = {
  TableName : "Article"
};

dynamodb.deleteTable(params, function(err, data) {
  if (err) {
    console.error("Unable to delete table. Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("Deleted table. Table description JSON:", JSON.stringify(data, null, 2));
  }
});

params = {
  TableName : "Article",
  KeySchema: [
    { AttributeName: "id", KeyType: "HASH"},  //Partition key
    { AttributeName: "slug", KeyType: "RANGE" }  //Sort key
  ],
  AttributeDefinitions: [
    { AttributeName: "id", AttributeType: "N" },
    { AttributeName: "slug", AttributeType: "S" }
  ],
  ProvisionedThroughput: {
    ReadCapacityUnits: 1,
    WriteCapacityUnits: 1
  }
};

dynamodb.createTable(params, function(err, data) {
  if (err) {
    console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
  } else {
    console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
  }
});

Insert a record in the table:

createArticle(body: any, cb: (newArticle) => any, err) {
  const key = {
    slug: body.slug,
    title: body.title
  };
  const value = {
    content: body.content
  };
  this.Article.insert(key, value).exec()
    .then(cb)
    .catch(err);
}

Deployment

Before deployment you need to configure zipping of node_modules and dist folders. There is an example of External Toole External tool

Then add this tool to the tasks before launching: Before launch

Install required packages:

npm i

Deploy packages by running

npm run sls-deploy

Warning

To get it working with aws-serverless-express you need to apply this patch