Your First Klotho App
This tutorial demonstrates how to create a Go REST API with chi/v5 and use Klotho to transform it into a cloud-native one.
The tutorial will cover the Klotho expose feature that give your existing code cloud native capabilities. We call these Klotho capabilities.
Getting Started
Prerequisites
- Klotho CLI installed
- Go 1.18
- Node.js 16.x+ (& NPM)
Application Overview
This sample app has 2 GET endpoints, and is primarily to showcase the experimental go expose capability.
REST API Endpoints
GET
/helloGET
/hello/{name}
This application will utilize the following annotations:
Setting Up
Start by creating a directory for your project.
mkdir klotho-my-first-app # Fill in wherever you'd like to create your application
cd klotho-my-first-app
Then create a go.mod
file in your project directory containing the following dependencies:
module example/klotho-my-first-app
go 1.18
require github.com/go-chi/chi/v5 v5.0.8
Then create a go.sum
file in your project directory containing the following:
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
Finally, create the main.go
file and add the following code, which will set up the chi/v5
router with the 2 GET
endpoints and add the @klotho::expose
annotation to http.ListenAndServe
:
package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from Klotho!"))
})
r.Get("/hello/{name}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf("Hello %s!", chi.URLParam(r, "name"))))
})
fmt.Println("Listening on :3000")
/* @klotho::expose {
* target = "public"
* id = "app"
* }
*/
http.ListenAndServe(":3000", r)
}
Testing locally
One of the most powerful aspects of Klotho is letting you code, debug and iterate locally, knowing that once your ready, the cloud version will behave the same way.
This gives you the fastest possible developer experience without changing how you work.
To run the application locally, in one terminal run:
go run main.go
Listening on :3000
Once the application is running, in another terminal test its endpoints with curl:
curl "localhost:3000/hello"
# -> Hello from Klotho!
curl "localhost:3000/hello/your-name"
# -> Hello your-name!
Once you're done testing, bring the server down (with Ctrl-C
in the first terminal).
Compiling with Klotho
Log into Klotho to set up your user profile. 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
With the Klotho capabilities added to the application, run Klotho to get the cloud native version of it.
❯ klotho . --app my-first-app --provider aws --outDir _compiled
██╗ ██╗██╗ ██████╗ ████████╗██╗ ██╗ ██████╗
██║ ██╔╝██║ ██╔═══██╗╚══██╔══╝██║ ██║██╔═══██╗
█████╔╝ ██║ ██║ ██║ ██║ ███████║██║ ██║
██╔═██╗ ██║ ██║ ██║ ██║ ██╔══██║██║ ██║
██║ ██╗███████╗╚██████╔╝ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
Adding resource input_file_dependencies:
Adding resource exec_unit:main
main.go:29:1: Found 2 route(s) on app 'r'
Adding resource gateway:app
Adding resource topology:my-first-app
Adding resource aws_template_data: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.
Visualizing the Cloud Version
Helping you understand what Klotho did, open the ./_compiled/my-first-app.png
diagram created alongside the cloud application:
The expose capability created and connected an API Gateway to the go-chi router app running on a Lambda execution unit.
Deploying the Application
Deployment Dependencies
Now that the last-section has produced infrastructure-as-code, it is possible to deploy the cloud-native version of the application. The AWS account set up on your computer will be used.
But first, we need to install and set up a few more dependencies:
- Docker
- an AWS account, set up with either:
- The
AWS_ACCESS_KEY_ID
&AWS_SECRET_ACCESS_KEY
environment variables for a user - OR,
$HOME/.aws/credentials
(eg. via AWS CLI:aws configure
) setup
- The
- Pulumi CLI
- Follow the Pulumi installation instructions and set up for local usage:
pulumi login --local
export PULUMI_CONFIG_PASSPHRASE=""
- Follow the Pulumi installation instructions and set up for local usage:
Deploying with Pulumi
# region should be an aws region, for example us-west-1
pulumi config set aws:region <region> --cwd '_compiled/' -s my-first-app
If this is your first time deploying this application, you will be prompted to create the my-first-app
stack. Just press ENTER and the stack will be created for you:
The stack 'my-first-app' does not exist.
If you would like to create this stack now, please press <ENTER>, otherwise press ^C:
Now that the Pulumi stack is created, simply install the project dependencies and deploy.
cd compiled
npm install
pulumi up
You'll see a preview of the changes to be applied to your AWS account: (you'll be able to delete these later on)
Previewing update (my-first-app):
Type Name Plan
+ pulumi:pulumi:Stack my-first-app-my-first-app create
+ ├─ awsx:ecr:Repository my-first-app create
+ │ └─ aws:ecr:LifecyclePolicy my-first-app create
+ ├─ aws:ecr:Repository my-first-app create
+ ├─ aws:cloudwatch:LogGroup main-function-api-lg create
+ ├─ aws:apigateway:RestApi app create
+ │ ├─ aws:apigateway:Resource app{name}/ create
+ │ │ └─ aws:apigateway:Method GET-{name}-6f360 create
+ │ │ └─ aws:apigateway:Integration lambda-GET-{name}-6f360 create
+ │ ├─ aws:apigateway:Method GET-/-8a5ed create
+ │ │ └─ aws:apigateway:Integration lambda-GET-/-8a5ed create
+ │ └─ aws:apigateway:Deployment app-deployment create
+ │ └─ aws:apigateway:Stage app-stage create
+ ├─ aws:iam:Role my-first-app_0d6e4_LambdaExec create
+ │ ├─ aws:iam:Policy my-first-app-main-exec create
+ │ └─ aws:iam:RolePolicyAttachment my-first-app-main-exec create
+ ├─ aws:iam:RolePolicyAttachment my-first-app-main-lambdabasic create
+ ├─ aws:lambda:Function main create
+ ├─ aws:s3:Bucket create
+ ├─ aws:lambda:Permission Get-name-permission create
+ └─ aws:lambda:Permission Get--permission create
Outputs:
apiUrls : [
[0]: output<string>
]
deploymentPortal: "None - Opted out of topology upload by default"
Resources:
+ 21 to create
Do you want to perform this update? [Use arrows to move, enter to select, type to filter]
> yes
no
details
Respond with yes
when you're ready and wait for your resources to be created. This usually takes a few minutes. When it's done deploying, you'll see the completion status and the AWS provided API Gateway URL for your API:
Outputs:
apiUrls : [
[0]: "https://qxy99avspl.execute-api.us-east-1.amazonaws.com/stage/"
]
Testing the Cloud Version
After you deploy the application, you'll have a publicly accessible endpoint. Test that the Klotho-powered version behaves the same as the local one rerunning the same tests from before, only this time point them at the deployed version:
APP_URL=<app url from the pulumi output> # including the /stage
curl "$APP_URL/hello"
# -> Hello from Klotho!
curl "$APP_URL/hello/your-name"
# -> Hello your-name!
Cleanup
To clean up your AWS deployment simply run:
cd compiled
pulumi destroy
You will see a Pulumi preview of all the resources pending deletion. Response with yes
to begin deleting.
What next?
- Join Discord and chat with us. What went well, what went poorly, what are you looking forward to?