Skip to main content

Your First Klotho App

tip

The examples ahead are written in JavaScript/TypeScript. We're currently working on support for Python, Go, Java, and C#.

This tutorial demonstrates how to create a plain Express.js REST API and use Klotho to transform it into a cloud-native one.

The tutorial will cover three core Klotho features that give your existing code cloud native capabilities. We call these Klotho capabilities.

Getting Started

Prerequisites

Before starting this tutorial, you must either install its prerequisites locally or you can get started more quickly by using our prepared Docker image.

Required Software

Application Overview

Your first Klotho application will be the doggie daycare sample application, (js-my-first-app). The doggie daycare application is an Express app that listens on port 3000.

The server has two REST endpoints:

  • POST /pets - Stores a new relationship between a pet and their owner in a key-value store
  • GET /pets - Returns all pet/owner relationships from the key-value store

This application utilizes the following annotations:

Repository

Clone our sample apps git repo and install the npm packages for the js-my-first-app app.

git clone https://github.com/klothoplatform/sample-apps.git
cd sample-apps/js-my-first-app
npm install

Compiling with Klotho

First, log in to Klotho. This will allow us to support you if you run into any issues, and give you the opportunity to shape the product in this early development stage.

klotho --login # if you haven't already

Now compile the application for AWS by running klotho and passing --provider aws as an argument on the command line.

klotho . --app my-first-app --provider aws
██╗  ██╗██╗      ██████╗ ████████╗██╗  ██╗ ██████╗
██║ ██╔╝██║ ██╔═══██╗╚══██╔══╝██║ ██║██╔═══██╗
█████╔╝ ██║ ██║ ██║ ██║ ███████║██║ ██║
██╔═██╗ ██║ ██║ ██║ ██║ ██╔══██║██║ ██║
██║ ██╗███████╗╚██████╔╝ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝

Adding resource input_file_dependencies:
Adding resource exec_unit:main
Found 2 route(s) on server 'app'
Adding resource gateway:pet-api
Adding resource persist_kv:petsByOwner
Adding resource topology:my-first-app
Adding resource infra_as_code:Pulumi (AWS)
Pulumi.my-first-app.yaml: Make sure to run `pulumi config set aws:region YOUR_REGION --cwd 'compiled/' -s 'my-first-app'` to configure the target AWS region.

The cloud version of the application is saved to the ./compiled directory, and has everything you need to deploy, run and operate the application.

Examining the Compiled Output

As part of the compilation, Klotho generates a high-level topology diagram showing the cloud resources that will be used in your application's cloud deployment and their relationships.

Open ./compiled/my-first-app.png to view the application's topology diagram:

topology diagram showing the resources

We can see here that Klotho has defined the following AWS topology:

  • main (Lambda) - The main Lambda function serves the Express app defined in js-my-first-app using a Lambda-compatible interface.
  • pet-api (API Gateway) - The pet-api API gateway is used to expose the Express routes defined in the main Lambda function.
  • petsByOwner (DynamoDB Table) - The petsByOwner DynamoDB table is used by the main Lambda function to store the relationships between pets and their owners.

Concepts

Execution Unit

In the Klotho world, the main Lambda function is what's referred to as an execution unit. An application is composed of one or more execution units, with each execution unit being responsible for executing a discrete portion of the application's code. When using AWS, execution units are backed by compute resources such as Lambda functions or Docker containers running on Fargate (ECS or EKS).

Because @klotho::execution_unit annotation is not used in this application, Klotho created a default execution unit for the application called main. When a default execution unit is created, Klotho determines its entrypoint by looking at the main field in the project's package.json file by default. However, the execution unit's entrypoint may be modified as a result of other Klotho annotations in the project (as we'll see in the next section).

Expose

Klotho was able to determine how to expose the application to the public using the @klotho::expose annotation on the Application's Express app.listen invocation.

index.js
/* @klotho::expose {
* id = "pet-api"
* target = "public"
* description = "Exposes the Pet API to the internet"
* }
*/
app.listen(3000, () => console.log('App listening locally on :3000'));

Annotating an Express app's app.listen invocation with @klotho::expose tells Klotho that you want to expose an Express application using an API Gateway, and also lets Klotho know that this Express app should be used as the entrypoint for all RESTful invocations of the execution unit serving it.

The target = "public" directive tells Klotho that this app should be accessible publicly on the internet.

Persist (Key-Value)

The final core Klotho concept used by this project is the persist capability denoted by the @klotho::persist annotation.This annotation has a different meaning depending on where it is used. When used to annotate an ES6 Map, Klotho will replace the Map with a DynamoDB-backed implementation of a key-value store during the compilation process so you can use the same Map semantics whether you are running the application locally or in the cloud.

For other ways to use @klotho::persist, check out our API reference.

As seen in the compiled output above, annotating the petsByOwner ES6 Map with @klotho::persist resulted in Klotho generating petsByOwner DynamoDB table.

index.js
/* @klotho::persist {
* id = "petsByOwner"
* }
*/
const petsByOwner = new Map();

Deploying the application

If you would like to deploy the application, refer to the deploying guide.

Testing Your Application

To test your endpoints, copy the api URL you see on your screen, from the end of the deploying step, and use these curl commands:

note

These curls are examples and should be replaced with calls that are relevant to your service.

APP_URL=<app url from the pulumi output> # including the /stage

$ curl $APP_URL/pets -X POST -d '{"owner":"aaron", "pet":"fred"}' -H"Content-Type: application/json"
Added fred as aaron's pet

$ curl $APP_URL/pets
{"aaron":"fred"}

What next?