
Why Hono?
Hono is a new framework compatible with almost any runtime that exists. Compared to traditional ExpressJS, it is easier to build applications for runtimes like Lambda or Cloudflare Workers using Hono. Hono provides adapters to ease this process. The syntax is quite similar to what we're used to in ExpressJS.
AWS CDK
In this tutorial, we will be using AWS CDK as our Infrastructure as a Service (IaaS) solution for building with AWS. While we could use a manual build and deploy process for Lambda, with CDK we can automate everything using code.
AWS CDK is a toolkit that allows you to define your AWS infrastructure using just code. It's similar to Terraform but not limited to a single coding language. It supports TypeScript, Java, C#, Python, and Go. You can choose the language you're most familiar with. For this tutorial, we will be using TypeScript. Enough intro, let's start. I'll explain the caveats part in the process.
1. Initialize AWS CDK project
mkdir hello-cdk && cd hello-cdk
Make sure you have AWS CDK installed in your machine, if not then follow this
cdk init app --language typescript
Your project will initialize like this
.
├── bin
│ └── hello-cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── hello-cdk-stack.ts
├── package.json
├── README.md
├── test
│ └── hello-cdk.test.ts
└── tsconfig.json
Now we switch our package manager to pnpm(my preference) and reinstall the dependency. You will see some default code generated. We leave it behind first.
2. Hono setup
Run this to create a hono app. Make sure to add .(dot)
for creating inside current folder. You will be prompted to choose adapters. Choose AWS Lambda - without edge.
pnpm create hono@latest .
pnpm create hono@latest .
create-hono version 0.18.0
✔ Using target directory … .
✔ Which template do you want to use? aws-lambda
✔ Directory not empty. Continue? Yes
✔ Do you want to install project dependencies? Yes
? Which package manager do you want to use? (Use arrow keys)
npm
bun
deno
❯ pnpm
yarn
Hono files has been created under src
folder
.
├── bin
│ └── hello-cdk.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── hello-cdk-stack.ts
├── package.json
├── pnpm-lock.yaml
├── README.md
├── src
│ └── index.ts
├── test
│ └── hello-cdk.test.ts
└── tsconfig.json
Inside index.ts
, hono has scaffold api for lambda, now we separate into app.local.ts
and app.ts
for Hono stuffs and into index.ts
for the application logic stuff.
For local testing, create this. Install @hono/node-server
import app from "./index";
import { serve } from "@hono/node-server";
serve(app);
console.log("Server running on port 3000")
Change index.ts to this. Export the app instance for lambda and local usage.
import { Hono } from "hono";
const app = new Hono();
app.get("/", (c) => {
return c.text("Hello Hono!");
});
export default app;
Create lambda.ts inside lib folder
import app from "../src/index";
import { handle } from "hono/aws-lambda";
export const handler = handle(app);
Now test the local. Run pnpm dev. Use tsx or bun for running typescript. curl or go to localhost:3000 to make sure the route is working. You will see Hello Hono! on your browser(or console).
AWS CDK
1. As our project initialize, we need to configure our CDK Stacks. This stack consist of infrastructure that we will use to run our lambda function.
import { Duration, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { Architecture, Runtime } from "aws-cdk-lib/aws-lambda";
import { LambdaRestApi } from "aws-cdk-lib/aws-apigateway";
import { NodejsFunction, SourceMapMode } from "aws-cdk-lib/aws-lambda-nodejs";
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
export class ApplicationStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const myLambdaFunction = new NodejsFunction(this, "my-lambda-function", {
functionName: "myLambdaFunctionName",
architecture: Architecture.ARM_64,
runtime: Runtime.NODEJS_20_X,
timeout: Duration.seconds(10),
memorySize: 256,
entry: "src/lambda.ts", // our lambda entrypoint
handler: "handler", // the function name
bundling: {
minify: true,
sourceMap: true,
sourceMapMode: SourceMapMode.INLINE,
sourcesContent: false,
target: "esnext",
},
environment: {
REGION: "ap-southeast-5" // leave if you have configured on the AWS CLI
},
});
const certificate = Certificate.fromCertificateArn(
this,
"your-aws-cert-name",
process.env.CERTIFICATE_ARN!
);
new LambdaRestApi(this, "my-api-gateway", {
restApiName: "my-api",
handler: myLambdaFunction,
proxy: true,
deployOptions: {
stageName: "prod",
},
domainName: {
domainName: "my-custom-domain.com",
certificate,
},
});
}
}
We create myLambdaFunction for our handler using NodejsFunction construct. Next attach the function to api gateway using LambdaRestApi construct. If you have custom domain, take the domain certificate ARN, create a cert and attach to the custom domain.
2. Install esbuild as our bundler for the lambda and cdk. pnpm add esbuild -D
3. Add config for esbuild in lib/config/config.json
{
"sourceMap": true,
"sourcesContent": true,
"target": "es2020",
"keepNames": true,
"stack": {
"dev": {
"minify": false
},
"prod": {
"minify": true
}
}
}