AWS Hackathon - 1Pitch / Pitch your startup to investors

Featured on Hashnode
AWS Hackathon - 1Pitch / Pitch your startup to investors

1Pitch - The 1-Minute pitch for startups.

My final submission after hard work.

This was a lot of learning and I would like to present you my submission for the AWS Amplify Hackathon hosted by Hashnode.

I am sorry in advance, that I probably rush this blog post and miss some points (it has been a tough last day), but anyway I will try my best to describe you what I have done in this application.

The idea

The idea of my project is to connect investors and startups with each other.

Those connection are based on the industry and stage of the startup, because investors are not interested in any kind of startups to push in some cash.

Startups get the chance to record a 1-minute pitch to present their idea in an audio recording.

I came to the idea, since I have myself trouble finding investments for my project. And finding an fitting investor, is a really hard task for founders. Also investors get like 1.000 requests in one year and have a tough job to revision all those applications.

There should be an easier way! That is the problem, 1Pitch is solving.

The Server: AWS Amplify, AppSync, Cognito and Lambda function

AWS Amplify

First of all its an ease to setup an project using Amplify. The CLI will mostly take care of IAM Roles in the AWS Service world. It generates CloudFormation templates for you, so you dont have to take care of it by yourself.

You can add services using the CLI like amplify add auth to add authentication to your app. You will have to go thru a set of options, which allows you to individualize auth settings for your application needs. I will not go into the details, as you can read them in the documentation of Amplify. (Follow this link: Amplify Documentation)

AppSync

You want to have an API for your application? Use AppSync!

Its an AWS Service which allows you to provide, both GraphQL and REST APIS. You can even combine those two with each other if necessary for your project needs. Using Amplify makes it easy to set up your AppSync Service. Run amplify add api and choose what kind of API you want AWS to host for you.

AppSync is not only your API provide, but also allows you to connect for example DynamoDB (which is an NoSQL database, that means it is no sequel, no relational database).

Even tho DynamoDB is no relational database, AppSync allows you to create connections based on your GraphQL schema. Which is super easy to handle, if your project is suitable with One2One, One2Many, Many2One and Many2Many.

This is a shorten part of my schema to show you how relational connections, between two types can be made. This is an many2many connection, which means in my example, that users can have many teams and teams can have many users.

type User @model {
  id: ID!
  email: AWSEmail
  firstname: String
  lastname: String
  avatar: S3Object
  bio: String
  teams: [TeamUserLink] @connection(keyName: "teamsByUser", fields: ["id"])
  createdAt: AWSDateTime
}

type TeamUserLink
  @model
  @key(name: "usersByTeam", fields: ["teamID"])
  @key(name: "teamsByUser", fields: ["userID"]) {
  id: ID!
  userID: ID!
  user: User @connection(fields: ["userID"])
  teamID: ID!
  team: Team @connection(fields: ["teamID"])
}

type Team @model {
  id: ID!
  members: [TeamUserLink] @connection(keyName: "usersByTeam", fields: ["id"])
  some: String
  fields: String
}

As you can see: I have used the @key directive to allow finding members in a team and another @key directive to find teams from a user. You might want to have a deeper dive into that. There is a lot content on YouTube by the Amplify team, which also helped me to understand those directives.

Cognito as your authentication and authorization is the way to go!

Cognito is a well managed authentication and authorization service by AWS, which allows you (especially in combination with Amplify) to add those easily to your application! You can have auth rules based on Cognito in your AppSync GraphQL schema to handle authorization for your application.

You can create triggers in your Cognito Console (on the left hand side you will find an menu point called "Triggers"). I have used the trigger "Post Confirmation" to execute an AWS Lambda function. This Lambda function creates an user (based on information from Cognito) in my database.

If your auth gets more complicated with for example multi-tenancy, there are couple of ways to go. I have chosen to go the most complicated way, haha! I would not really recommend you to follow my approach, but I will give you an insight on how you could manage authorization using AWS Lambda.

You are able to create Lambda function using the Amplify CLI. Run amplify add function in your terminal and go thru the steps. You need to define which resources you want to access with your Lambda trigger (for example the Bookmark table, with create, read, update and delete IAM permissions).

AWS Lambda functions

You would like to combine your schema and your Lambda functions using the @function directive. This allows you to mount functions to any type, field, query and mutation.

Here is an example what I have used in my project:

type Mutation {
  createBookmark(input: CreateBookmarkInput!): Bookmark
    @function(name: "1PitchTeam-${env}")
  updateBookmark(input: UpdateBookmarkInput!): Bookmark
    @function(name: "1PitchTeam-${env}")
  deleteBookmark(input: DeleteBookmarkInput!): Bookmark
    @function(name: "1PitchTeam-${env}")
}

When you use the @function directive, you need to name your function. It might be the case that you have several environments (for example dev and prod). You can use ${env} to stay consistent and save efforts changing your schema for each environment. Which you might be missing something for change.

Now that we have our mutations connected with our schema, lets have a look what I have done in my Lambda function.

/* Amplify Params - DO NOT EDIT
    API_1PITCH_BOOKMARKTABLE_ARN
    API_1PITCH_BOOKMARKTABLE_NAME
    ...
*/

const readModel = require('./helpers/readModel')
const updateModel = require('./helpers/updateModel')
const deleteModel = require('./helpers/deleteModel')

exports.handler = async (event, _, callback) => {
  const {
    fieldName,
    identity,
    arguments: { input }
  } = event

  console.log('Received request for Lambda:', fieldName)
  switch (fieldName) {
    case 'createBookmark':
      if (!(await authorizerInvestor(input.investorID, identity, callback)))
        break
      else return createBookmark(input)
    case 'updateBookmark':
      if (!(await authorizerBookmark(input.id, identity, callback))) break
      else return updateModel(input, process.env.API_1PITCH_BOOKMARKTABLE_NAME)
    case 'deleteBookmark':
      if (!(await authorizerBookmark(input.id, identity, callback))) break
      else return deleteModel(input, process.env.API_1PITCH_BOOKMARKTABLE_NAME)
  }
}

In you lambda trigger you have access to several information. First of all you can check the identity of the user. You will get an object, which includes information from Cognito. You also have access to the arguments passed in your schema. In my example its the input that I want to access and check and run logic against it.

In my project investors have bookmarks, so my application knows, who they already have contacted and which startup they are not interested in.

This logic looks like this:

const readModel = require('../helpers/readModel')

const authorizerBookmark = async (bookmarkID, identity, callback) => {
  try {
    const bookmark = await readModel(
      bookmarkID,
      process.env.API_1PITCH_BOOKMARKTABLE_NAME
    )
    const investor = await readModel(
      bookmark[0].investorID,
      process.env.API_1PITCH_INVESTORTABLE_NAME
    )
    const members = await readModel(
      investor[0].teamID,
      process.env.API_1PITCH_TEAMUSERLINKTABLE_NAME,
      'teamID',
      'usersByTeam'
    )

    const isAuthorized = members.some(
      ({ userID, admin }) => userID === identity.sub && admin
    )
    if (!isAuthorized) {
      callback(null, {
        errorMessage: 'You are not authorized to perform this action',
        errorType: 'UNAUTHORIZED'
      })
      return false
    }
    return true
  } catch (error) {
    console.log('DynamoDB Error: ', error)
    return null
  }
}

module.exports = authorizerBookmark

This looks a bit complicated, but as I have said. You dont need to follow the same approach for authorization as I have.

In my authorization, I check who is trying to create that Bookmark and if is that person allowed to perform this action. Read thru the code slowly and you might understand what is happening there.

The mobile application

For the mobile application I have used react-native with Expo. Which makes it easy to create cross-platform mobile applications.

Amplify is seamlessly working with your frontend application. Amplify codegen generates GraphQL queries, mutations and subscriptions which you can use for your API calls. API calls can be made using the aws-amplify sdk.

I will not go into too much details for the mobile application, since its a really big topic and you can learn this on YouTube, Udemy or some other online resources.

Git repo

You find the whole code of my application on GitHub.

If you have Expo you can try to access my react-native app here: Expo 1Pitch

Final thoughts

It was really tough for me to create this project and have a working MVP to present for the Hackathon. There is a lot to improve, which I will do after the hackathon.

Please dont go wild with me on the react-native application. My time ran out and I really needed to rush to make a submission until the deadline of this hackathon.

I would like to shout out to some of my supporters and persons who were great help during this hackathon. Chai, uncodable, omerT and andthensumm. Also big shout outs to Nader Dabit and the whole AWS Amplify Team for their great resources on YouTube and Twitch. Those information made it possible for me to understand Amplify, AppSync and other AWS services. Special thanks go out to my girlfriend, who supported me by delivering food to my office and take care of me.

If you have questions about Amplify, join the Discord. I hang out there quite a lot.

If you liked my post, please like it, comment it and follow me on Twitter: @ReneGoretzka