booster

Getting started

Installing Booster

You can develop with Booster using any of the following operating systems:

Booster Prerequisites

Install Node.js

The minimal required Node.js version is v14.14. Download the installer from nodejs website, or install it using your system’s package manager.

Ubuntu

Just run the following commands on the terminal

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt install nodejs
macOS

Using Homebrew package manager, run the following command on the terminal

brew install node
Windows

Using Chocolatey package manager, run the following command in your PowerShell

choco install nodejs

Verify that it was installed properly by checking so from your terminal:

$ node -v
v14.14.0

$ npm -v
7.0.0

As soon as you have a Node.js version higher than v14.14, and an npm version higher than 7, you are good to go. Just note that npm comes with node, you don’t have to install it apart.

Alternatively, we recommend you to use a version manager for dealing with different Node.js versions:

Install Git

Booster will initialize a Git repository when you create a new project (unless you use the --skipGit flag), so it is required that you have it already installed in your system.

Ubuntu
sudo apt install git-all
macOS
brew install git
Windows
choco install git
Git configuration variables

After installing git in your machine, make sure that user.name and user.email are properly configured. Take a look at the Git configuration page for more info.

To configure them, run in your terminal:

git config --global user.name "Your Name Here"
git config --global user.email "your_email@youremail.com"

AWS Provider Prerequisites

This step is optional; Booster is a cloud-native framework, meaning that your application will be deployed to the cloud using different cloud providers. By default, Booster uses the AWS Provider, so you need an AWS account.

[!WARNING] Booster is free to use, but remember that the resources deployed to your cloud provider might generate some expenses.

In AWS, all the resources generated by Booster are part of the AWS free tier. When you’re not eligible for the free tier, resources are charged on-demand. Deploying a Booster project and sending a few commands and queries should cost just a few cents.

In any case, make sure to un-deploy your application with the command boost nuke -e production if you’re not planning to keep using it.

Now it is a good time to create that AWS account, you can do so from the AWS console registration.

Once you’ve registered yourself, you have to generate an access key for Booster. To do so, login into the AWS Console, and click on your account name on the top-right corner.

aws account menu location

A menu will open, click on My security credentials and it will take you to the Identity and Access Management panel. Once there, create an access key:

create access key button location

A pop-up like the following will appear, don’t close it!.

credentials pop up

Using data from that pop-up, create a folder called .aws under your home folder, and a file inside called credentials with this syntax:

  ~/.aws/credentials
[default]
aws_access_key_id = <YOUR ACCESS KEY ID>
aws_secret_access_key = <YOUR SECRET ACCESS KEY>

You can see more details in the Providers configuration section.

Multiple AWS Accounts

Declaring Region

If you’d like to use multiple AWS account profiles (in place of or in addition to your default profile) you will need to also set a region option for each one to let the AWS SDK know which region to deploy to. To do this, you have two options:

  ~/.aws/credentials
[default]
aws_access_key_id = <DEFAULT ACCESS KEY ID>
aws_secret_access_key = <DEFAULT SECRET ACCESS KEY>
region=<DEFAULT REGION>

[other_profile] Give this profile the name that works best for you
aws_access_key_id = <YOUR ACCESS KEY ID>
aws_secret_access_key = <YOUR SECRET ACCESS KEY>
region=<REGION FOR YOUR BOOSTER APP>
  ~/.aws/config
[default]
region=<DEFAULT REGION>

[profile other_profile] You can rename the profile in any way that works for you
region=<REGION FOR YOUR BOOSTER APP>

Switching Profiles

To change the AWS profile you use to deploy or nuke you need to export the profile in your current AWS_PROFILE environment variable. This is done within your current terminal session with the following command –

$ export AWS_PROFILE=other_profile

[!WARNING] When you are switching profiles by setting the AWS_PROFILE environment variable, you will need to ensure you do not have values curently set for either the AWS_SECRET_ACCESS_KEY or AWS_ACCESS_KEY_ID variables. If these have values assigned, they will take precedence over any keys set within the current AWS_PROFILE profile.

You can check if AWS_SECRET_ACCESS_KEY or AWS_ACCESS_KEY_ID have variables assigned by checking all current AWS variables -

$ env | grep AWS

Or by checking these variables explicitly –

$ echo $AWS_ACCESS_KEY_ID
$ echo $AWS_SECRET_ACCESS_KEY

If values are present for AWS_SECRET_ACCESS_KEY or AWS_ACCESS_KEY_ID you can clear them by unsetting them –

$ unset AWS_SECRET_ACCESS_KEY
$ unset AWS_ACCESS_KEY_ID

Selecting different profiles can be done manually, for each deploy or nuke, as shown above, or you can create scripts within your package.json to set these values at the same time as you deploy or nuke an environment. For example, below we are creating scripts that set a different AWS profile to be used for ‘development’ and ‘production environments –

~/package.json
...
"scripts": {
    "deploy-dev": "AWS_PROFILE=profileOne boost deploy -e development",
    "deploy-prod": "AWS_PROFILE=profileTwo boost deploy -e production",
    "nuke-dev": "AWS_PROFILE=profileOne boost nuke -e development",
    "nuke-prod": "AWS_PROFILE=profileTwo boost nuke -e production",
    "lint:check": "eslint --ext '.js,.ts' **/*.ts",
    "lint:fix": "eslint --quiet --fix --ext '.js,.ts' **/*.ts",
    "build": "ttsc -b tsconfig.json",
    "clean": "rimraf ./dist tsconfig.tsbuildinfo",
    "test": "AWS_SDK_LOAD_CONFIG=true BOOSTER_ENV=test nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\""
  },
...

These custom scripts are used in the same fashion as other scripts, for example –

npm run deploy-dev

Azure Provider Prerequisites

This step is only necessary in the case that you want to use the Azure Provider.

[!NOTE] Azure has experimental support and the team is currently improving them.

[!WARNING]

Booster is free to use, but remember that the resources deployed to your cloud provider might generate some expenses.

In Azure, when you’re not eligible for the free tier, resources are charged on-demand. Deploying a Booster project and sending a few commands and queries should cost just a few cents.

In any case, make sure to un-deploy your application with the command boost nuke -e production if you’re not planning to keep using it.

Now is a good time to create that Microsoft Azure account. You can do so from the Microsoft SingUp page. Then you can go to your Azure account in this link.

Once you have created the Azure account, you need to install the Azure CLI on your computer, by following the instructions in the Azure CLI documentation.

Apart from that, you need to install jq on your system.

The final step consists of signing in to your Azure account from the Azure CLI:

az login

You can check the different login options in this reference document.

Once you are logged in from the CLI, you can deploy applications with Booster.

You can see more details in the Providers configuration section.

Kubernetes Provider Prerequisites

This step is only necessary in the case that you want to use the Kubernetes provider.

[!NOTE] Kubernetes has experimental support and the team is currently improving them.

The main requirement is having a Kubernetes Cluster already configured. This provider has been succesfully tested in EKS (Amazon Elastic Kubernetes Service), AKS (Azure Kubernetes Service) and GKE (Google Kubernetes Engine). Since Kubernetes is a standard, this provider can also work in other Kubernetes clusters, including on-premises configurations.

[!WARNING] Booster is free to use, but remember that the Kubernetes cluster resources used for the deployment might generate some expenses.

Additionally, it is also required to install kubectl and Helm. The required Helm version is 3 or greater.

Please note that the desired cluster should be accessible from the kubectl command and you can successfully run:

kubectl get pods -A

You can see more details in the Providers configuration section.

Installing the Booster CLI

Booster comes with a command-line tool that helps you generating boilerplate code, testing and deploying the application, and deleting all the resources in the cloud. All the stable versions are published to npm, these versions are the recommended ones, as they are well documented, and the changes are stated in the release notes.

To install the Booster CLI run this:

npm install --global @boostercloud/cli

Verify the Booster CLI installation with the boost version command. You should get back something like

$ boost version
@boostercloud/cli/0.16.1 darwin-x64 node-v14.14.0

Your first Booster app in 10 minutes

In this section, we will go through all the necessary steps to have the backend up and running for a blog application in just a few minutes. The steps to follow will be:

1. Create the project

First of all, we will use the Booster generators to create a project. Run this command boost new:project boosted-blog and follow the instructions. After some prompted questions, the CLI will ask you to select one of the available providers to set up as the main provider that will be used.

? What's the package name of your provider infrastructure library? (Use arrow keys)
❯ @boostercloud/framework-provider-aws (AWS)
  @boostercloud/framework-provider-azure (Azure) [Experimental]
  @boostercloud/framework-provider-kubernetes (Kubernetes) [Experimental]
  Other

When asked for the provider, select AWS as that is what we have configured here for the example. You can use another provider if you want, or add more providers once you have created the project.

After choosing your provider, you will see your project generated!:

> boost new:project boosted-blog

...

ℹ boost new 🚧
✔ Creating project root
✔ Generating config files
✔ Installing dependencies
ℹ Project generated!

[!TIP] If you prefer to create the project with default parameters, you can run the command as boost new:project booster-blog --default. The default parameters are as follows:

In case you want to specify each parameter without following the instructions, you can use the following flags with this structure <flag>=<parameter>.

Flag Short version Description
--homepage -H The website of this project
--author -a Author of this project
--description -d A short description
--license -l License used in this project
--providerPackageName -p Package name implementing the cloud provider integration where the application will be deployed
--repository -r The URL of the repository
--version -v The initial version

Additionally, you can use the --skipInstall flag if you want to skip installing dependencies and the --skipGit flag in case you want to skip git initialization.

Booster CLI commands follow this structure: boost <subcommand> [<flags>] [<parameters>]. Let’s break down the command we have just executed:

[!TIP] You can always use the --help flag to get all the available options for each cli command.

When finished, you’ll see some scaffolding that has been generated. The project name will be the project’s root so cd into it:

cd boosted-blog

There you should have these files and directories already generated:

boosted-blog
├── .eslintignore
├── .gitignore
├── .eslintrc.js
├── .prettierrc.yaml
├── package-lock.json
├── package.json
├── src
│   ├── commands
│   ├── common
│   ├── config
│   │   └── config.ts
│   ├── entities
│   ├── events
│   ├── event-handlers
│   ├── read-models
│   └── index.ts
├── tsconfig.eslint.json
└── tsconfig.json

Now open the project in your favorite editor, e.g. Visual Studio Code.

2. First command

Commands define the input to our system, so we’ll start by generating our first command to create posts. Use the command generator, while in the project’s root directory, as follows:

boost new:command CreatePost --fields postId:UUID title:string content:string author:string

The new:command generator creates a create-post.ts file in the commands folder:

boosted-blog
└── src
    └── commands
        └── create-post.ts

As we mentioned before, commands are the input of our system. They’re sent by the users of our application. When they are received you can validate its data, execute some business logic, and register one or more events. Therefore, we have to define two more things:

  1. Who is authorized to run this command.
  2. The events that it will trigger.

Booster allows you to define authorization strategies (we will cover that later). Let’s start by allowing anyone to send this command to our application. To do that, open the file we have just generated and add the string 'all' to the authorize parameter of the @Command decorator. Your CreatePost command should look like this:

@Command({
  authorize: 'all', // Specify authorized roles here. Use 'all' to authorize anyone
})
export class CreatePost {
  public constructor(
    readonly postId: UUID,
    readonly title: string,
    readonly content: string,
    readonly author: string
  ) {}

  public static async handle(command: CreatePost, register: Register): Promise<void> {
    register.events(/* YOUR EVENT HERE */)
  }
}

3. First event

Instead of creating, updating, or deleting objects, Booster stores data in the form of events. They are records of facts and represent the source of truth. Let’s generate an event called PostCreated that will contain the initial post info:

boost new:event PostCreated --fields postId:UUID title:string content:string author:string

The new:event generator creates a new file under the src/events directory. The name of the file is the name of the event:

boosted-blog
└── src
    └── events
        └── post-created.ts

All events in Booster must target an entity, so we need to implement an entityID method. From there, we’ll return the identifier of the post created, the field postID. This identifier will be used later by Booster to build the final state of the Post automatically. Edit the entityID method in events/post-created.ts to return our postID:

// src/events/post-created.ts

@Event
export class PostCreated {
  public constructor(
    readonly postId: UUID,
    readonly title: string,
    readonly content: string,
    readonly author: string
  ) {}

  public entityID(): UUID {
    return this.postId
  }
}

Now that we have an event, we can edit the CreatePost command to emit it. Let’s change the command’s handle method to look like this:

// src/commands/create-post.ts::handle
public static async handle(command: CreatePost, register: Register): Promise<void> {
  register.events(new PostCreated(command.postId, command.title, command.content, command.author))
}

Remember to import the event class correctly on the top of the file:

import { PostCreated } from '../events/post-created'

We can do any validation in the command handler before storing the event, for our example, we’ll just save the received data in the PostCreated event.

4. First entity

So far, our PostCreated event suggests we need a Post entity. Entities are a representation of our system internal state. They are in charge of reducing (combining) all the events with the same entityID. Let’s generate our Post entity:

boost new:entity Post --fields title:string content:string author:string --reduces PostCreated

You should see now a new file called post.ts in the src/entities directory.

This time, besides using the --fields flag, we use the --reduces flag to specify the events the entity will reduce and, this way, produce the Post current state. The generator will create one reducer function for each event we have specified (only one in this case). Reducer functions in Booster work similarly to the reduce callbacks in Javascript: they receive an event and the current state of the entity, and returns the next version of the same entity. In this case, when we receive a PostCreated event, we can just return a new Post entity copying the fields from the event. There is no previous state of the Post as we are creating it for the first time:

// src/entities/post.ts
@Entity
export class Post {
  public constructor(public id: UUID, readonly title: string, readonly content: string, readonly author: string) {}

  @Reduces(PostCreated)
  public static reducePostCreated(event: PostCreated, currentPost?: Post): Post {
    return new Post(event.postId, event.title, event.content, event.author)
  }
}

Entities represent our domain model and can be queried from command or event handlers to make business decisions or enforcing business rules.

5. First read model

In a real application, we rarely want to make public our entire domain model (entities) including all their fields. What is more, different users may have different views of the data depending on their permissions. That’s the goal of ReadModels. Client applications can query or subscribe to them.

Read models are projections of one or more entities into a new object that is reachable through the query and subscriptions APIs. Let’s generate a PostReadModel that projects our Post entity:

boost new:read-model PostReadModel --fields title:string author:string --projects Post:id

We have used a new flag, --projects, that allow us to specify the entities (can be many) the read model will watch for changes. You might be wondering what is the :id after the entity name. That’s the joinKey, but you can forget about it now.

As you might guess, the read-model generator will create a file called post-read-model.ts under src/read-models:

boosted-blog
└── src
    └── read-models
        └── post-read-model.ts

There are two things to do when creating a read model:

  1. Define who is authorized to query or subscribe it
  2. Add the logic of the projection functions, where you can filter, combine, etc., the entities fields.

While commands define the input to our system, read models define the output, and together they compound the public API of a Booster application. Let’s do the same we did in the command and authorize all to query/subscribe the PostReadModel. Also, and for learning purposes, we will exclude the content field from the Post entity, so it won’t be returned when users request the read model.

Edit the post-read-model.ts file to look like this:

// src/read-models/post-read-model.ts
@ReadModel({
  authorize: 'all', // Specify authorized roles here. Use 'all' to authorize anyone
})
export class PostReadModel {
  public constructor(public id: UUID, readonly title: string, readonly author: string) {}

  @Projects(Post, 'id')
  public static projectPost(entity: Post, currentPostReadModel?: PostReadModel): ProjectionResult<PostReadModel> {
    return new PostReadModel(entity.id, entity.title, entity.author)
  }
}

6. Deployment

At this point, we’ve:

With this, you already know the basics to build event-driven, CQRS-based applications with Booster.

You can check that code compiles correctly by running the build command:

boost build

You can also clean the compiled code by running:

boost clean

Now, let’s run our application to see it working. It is as simple as running:

boost start -e local

Or, we can deploy our application to the cloud with no additional changes by running the deploy command:

boost deploy -e production

This is the Booster magic! ✨ When running the start or the deploy commands, Booster will handle the creation of all the resources, like Lambdas, API Gateway, and the “glue” between them; permissions, events, triggers, etc. It even creates a fully functional GraphQL API!

[!NOTE] Deploy command automatically builds the project for you before performing updates in the cloud provider, so, build command it’s not required beforehand.

With -e production we are specifying which environment we want to deploy. We’ll talk about them later.

[!TIP] If at this point you still don’t believe everything is done, feel free to check in your provider’s console. You should see, as in the AWS example below, that the stack and all the services are up and running! It will be the same for other providers. 🚀

resources

When deploying, it will take a couple of minutes to deploy all the resources. Once finished, you will see information about your application endpoints and other outputs. For this example, we will only need to pick the output ending in httpURL, e.g.:

https://<some random number>.execute-api.us-east-1.amazonaws.com/production

[!NOTE] By default, the full error stack trace is send to a local file, ./errors.log. To see the full error stack trace directly from the console, use the --verbose flag.

7. Testing

Let’s get started testing the project. We will perform three actions:

Booster applications provide you with a GraphQL API out of the box. You send commands using mutations and get read models data using queries or subscriptions.

In this section, we will be sending requests by hand using the free Altair GraphQL client, which is very simple and straightforward for this guide. However, you can use any client you want. Your endpoint URL should look like this:

<httpURL>/graphql

7.1 Creating posts

Let’s use two mutations to send two CreatePost commands.

mutation {
  CreatePost(
    input: {
      postId: "95ddb544-4a60-439f-a0e4-c57e806f2f6e"
      title: "Build a blog in 10 minutes with Booster"
      content: "I am so excited to write my first post"
      author: "Boosted developer"
    }
  )
}
mutation {
  CreatePost(
    input: {
      postId: "05670e55-fd31-490e-b585-3a0096db0412"
      title: "Booster framework rocks"
      content: "I am so excited for writing the second post"
      author: "Another boosted developer"
    }
  )
}

The expected response for each of those requests should be:

{
  "data": {
    "CreatePost": true
  }
}

[!NOTE] In this example, the IDs are generated on the client-side. When running production applications consider adding validation for ID uniqueness. For this example, we have used a UUID generator

7.2 Retrieving all posts

Let’s perform a GraphQL query that will be hitting our PostReadModel:

query {
  PostReadModels {
    id
    title
    author
  }
}

It should respond with something like:

{
  "data": {
    "PostReadModels": [
      {
        "id": "05670e55-fd31-490e-b585-3a0096db0412",
        "title": "Booster framework rocks",
        "author": "Another boosted developer"
      },
      {
        "id": "95ddb544-4a60-439f-a0e4-c57e806f2f6e",
        "title": "Build a blog in 10 minutes with Booster",
        "author": "Boosted developer"
      }
    ]
  }
}

7.3 Retrieving specific post

It is also possible to retrieve specific a Post by adding the id as input, e.g.:

query {
  PostReadModel(id: "95ddb544-4a60-439f-a0e4-c57e806f2f6e") {
    id
    title
    author
  }
}

You should get a response similar to this:

{
  "data": {
    "PostReadModel": {
      "id": "95ddb544-4a60-439f-a0e4-c57e806f2f6e",
      "title": "Build a blog in 10 minutes with Booster",
      "author": "Boosted developer"
    }
  }
}

8. Removing the stack

It is convenient to destroy all the infrastructure created after you stop using it to avoid generating cloud resource costs. Execute the following command from the root of the project. For safety reasons, you have to confirm this action by writing the project’s name, in our case boosted-blog that is the same used when we run new:project CLI command.

> boost nuke -e production

? Please, enter the app name to confirm deletion of all resources: boosted-blog

Congratulations! You’ve built a serverless backend in less than 10 minutes. We hope you have enjoyed discovering the magic of the Booster Framework.

9. More functionalities

This is a really basic example of a Booster application. The are many other features Booster provides like:

Continue reading to dig more. You’ve just scratched the surface of all the Booster capabilities!

Examples and walkthroughs

Creation of a question-asking application backend

In the following video, you will find how to create a backend for a question-asking application from scratch. This application would allow users to create questions and like them. This video goes from creating the project to incrementally deploying features in the application. You can find the code both for the frontend and the backend in this GitHub repo.

All the guides and examples

Check out the step-by-step guides and the example apps repository to see Booster in use.