Deploy a REST API using Serverless, typeScript, Express and Node.js
Serverless를 사용하기 위해선 Serverless Framework 설치와, AWS credentials이 필요하다.
$ mkdir my-express-application && cd my-express-application
$ npm init -f
새로운 package.json file과 함께 directory 생성한다.
$ npm install --save express serverless-http
express framework와 serverless-http를 설치한다. serverless-http packages는 Node.js 어플리케이션과 API Gateway 간의 인터페이스를 처리하는 미들웨어다.
API를 쉽게 생성, 게시, 유지 관리, 모니터링 및 보호할 수 있는 완전 관리형 서비스이다. API는 애플리케이션이 백엔드 서비스의 데이터, 비즈니스 로직 또는 기능에 액세스할 수 있도록 하는 창구 역할을 한다. 자세하게 설명하자면, API Gateway는 API 백엔드 서비스와 API 사용자 사이에 위치하여 API 엔드포인트에 대한 HTTP 요청을 처리하고 올바른 백엔드로 라우팅한다. API 정의 및 엔드포인트와 해당 백엔드 서비스 간의 매핑을 관리하는 데 도움이 되는 도구 세트를 제공한다. 또한 API 참조를 생성하고 API 문서로 사용자에게 제공할 수 있다. AWS Lambda, AWS SNS, AWS IAM 등과 같은 다른 많은 AWS 서비스와 통합된다.
이렇듯 API Gateway는 서버리스 기능과 API 정의를 연결하는 부분이다. HTTP 요청에 대한 응답으로 직접 서버리스 기능 실행을 트리거할 수 있기 때문에 서버리스 설정에서 필수적인 이유이다.
간단한 HTTP API의 경우 serverless.yml 파일에서 바로 서버리스 기능에 연결할 API 게이트웨이 엔드포인트를 지정한다.
serverless.yml
functions:
index:
handler: handler.hello
events:
- http: GET hello # this is the API Gateway event
여기서, API request는 handler.hello
handler로 전달되고 Serverless function을 이용가능하게 만들어준다.
API Gateway 해당 주소에서 API Gateway 예시를 더 많이 확인할 수 있다.
const serverless = require('serverless-http');
const express = require('express')
const app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
module.exports.handler = serverless(app);
root path /
에 요청이 들어오면 간단하게 “Hello World”를 띄울 수 있는 코드다. serverless-http package
를 첫번째 줄에서 import하였고, handler 함수를 export 하고 있다.
배포를 위해서 serverless.yml
파일을 설정해야 한다.
# serverless.yml
service: my-express-application // project 이름
provider:
name: aws
runtime: nodejs6.10
stage: dev
region: us-east-1 // 배포할 region
functions:
app:
handler: index.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
기본적인 구성이다. app이라는 함수를 만들었고 이 함수는 index.js에서 exported된 함수인 handler를 사용하고 있다. 이 과정에서 API Gatway와 연결이 되었고, HTTP trigger로 구성되었다.
$ sls deploy
... snip ...
Service Information
service: my-express-application
stage: dev
region: us-east-1
stack: my-express-application-dev
api keys:
None
endpoints:
ANY - https://bl4r0gjjv5.execute-api.us-east-1.amazonaws.com/dev
ANY - https://bl4r0gjjv5.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
app: my-express-application-dev-app
deploy를 하면 endpoints가 생성되고, 해당 주소로 테스트 할 수 있다.
# serverless.yml
functions:
app:
handler: index.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
getUser:
handler: index.handler
events:
- http: 'GET /users/{proxy+}'
createUser:
handler: index.handler
events:
- http: 'POST /users'
GET /users/:userId
로 들어오는 모든 request들은 getUser
instance에 의해 처리되고, POST /users/
로 들어오는 모든 request들은 createUser
instance에 의해 처리된다. 다른 request들은 main 함수인 app
instance에 의해 처리될 것이다.
HTTP 엔드포인트를 AWS Lambda 함수의 이벤트 소스로 생성하려면 Serverless Framework의 간편한 AWS API Gateway Events 구문을 사용할 수 있다. AWS Lambda 함수와 통합하도록 HTTP 엔드포인트를 구성할 수 있는 방법은 5가지(lambda-proxy
/ aws-proxy
/ aws_proxy
(권장), lambda
/aws
, http
, http-proxy
/http_proxy
, mock
)가 있지만, default로 lambda-proxy
방법을 사용하는 것을 권장한다. lambda-proxy
는 HTTP request를 자동적으로 AWS Lambda function(headers, body, etc.)으로 전달하고 response(headers, status code, body)를 AWS Lambda 함수의 코드에 담을 수 있도록 허락한다. 반면에 lambda
의 방식은 header, status codes 등을 각각 API Gateway Endpoint(not in code)에 정의하도록 하기 때문에 지양하는 것을 권한다.
이벤트는 함수가 실행되도록 하는 trigger다. AWS를 사용 중이라면 AWS 안에 있는 모든 events는 AWS Lambda function을 실행시킬 수 있다. (e.g. S3 bucket upload, HTTP endpoints created via API Gateway)
API Gateway REST API를 특정 리전과 연결하기 위한 리전 엔드포인트를 지원한다.
service: my-service
provider:
name: aws
endpointType: REGIONAL
functions:
hello:
events:
- http:
path: user/create
method: get
API Gateway를 사용해 HTTP API를 배포할 수 있다.
# Event Definition
# General setup
functions:
simple:
handler: handler.simple
events:
- httpApi: 'PATCH /elo'
extended:
handler: handler.extended
events:
- httpApi:
method: POST
path: /post/just/to/this/path
# Parameters
functions:
params:
handler: handler.params
events:
- httpApi:
method: GET
path: /get/for/any/{param}
functions:
# lambda에서 표시되는 함수 이름
function1:
# 등록할 함수 경로
handler(임의): src/controllers/...
description: if needed
# API Gateway 설정
events:
- httpApi:
method: POST
path: /user/mypage (example)
function2:
handler: src/service/..
description: ...
함수 이름을 지정하고, 등록할 함수 경로를 설정한다. 필요에 의해 description을 달 수도 있다. events 설정을 하여 method
와 path
를 지정해줄 수 있다.
Local development configuration with Serverless offline plugin
로컬 개발과 테스트를 위해 API 게이트웨이 환경을 설정한다.
$ npm install --save-dev serverless-offline
install 한 다음 serverless.yml에 plugin을 추가해준다.
# serverless.yml
plugins:
- serverless-offline
start the serverless-offline server:
$ sls offline start
Serverless: Starting Offline: dev/us-east-1.
Serverless: Routes for app:
Serverless: ANY /
Serverless: ANY /{proxy*}
Serverless: Routes for getUser:
Serverless: GET /users/{proxy*}
Serverless: Routes for createUser:
Serverless: POST /users
Serverless: Offline listening on http://localhost:3000$ sls offline startServerless: Starting Offline: dev/us-east-1. Serverless: Routes for app:Serverless: ANY /Serverless: ANY /{proxy*} Serverless: Routes for getUser:Serverless: GET /users/{proxy*} Serverless: Routes for createUser:Serverless: POST /users Serverless: Offline listening on http://localhost:3000
환경변수를 호출하여 쓸 수 있다.
plugins:
- serverless-dotenv-plugin
...
...
provider:
name: aws
runtime: nodejs6.10
stage: ${env:STAGE}
region: ${env:AWS_REGION}
...
Typescript 지원을 위한 서버리스 플러그인
yarn add --dev serverless-plugin-typescript typescript
# or
npm install -D serverless-plugin-typescript typescript
# Add the following plugin to your serverless.yml:
plugins:
- serverless-plugin-typescript
# tsconfig.json
{
"compilerOptions": {
"preserveConstEnums": true,
"strictNullChecks": true,
"sourceMap": true,
"allowJs": true,
"target": "es5",
"outDir": ".build",
"moduleResolution": "node",
"lib": ["es2015"],
"rootDir": "./"
}
}
.gitignore와 같은 파일을 무시하는 서버리스 플러그인
$ npm install --save-dev serverless-ignore
Add serverless-ignore to your plugins list (serverless.yml)
plugins:
- serverless-ignore
Add a .slsignore in your root folder with a .gitignore-like syntax with all the files you want to ignore
Example:
# it works like a .gitignore
# for sls
README.md
*.log
# ignore aws-sdk
node_modules/aws-sdk/*
.env.example
.git/*
__tests__/*
# it works like a .gitignore# for slsREADME.md*.log # ignore aws-sdknode_modules/aws-sdk/* .env.example.git/*__tests__/*
AWS Lambda가 Serverless로 배포할 수 있는 사용자 지정 도메인 이름을 만든다. 도메인 이름을 배포 및 삭제할 때 기본 경로 매핑을 허용한다.
$ npm install serverless-domain-manager --save-dev
plugins:
- serverless-domain-manager # Add the plugin configuration
custom:
customDomain:
domainName: serverless.foo.com
stage: ci
basePath: api
certificateName: '*.foo.com'
createRoute53Record: true
endpointType: 'regional'
securityPolicy: tls_1_2
apiType: rest
autoDomain: false
Running
To create the custom domain:
$ serverless create_domain
To deploy with the custom domain:
$ serverless deploy
To remove the created custom domain:
$ serverless delete_domain
위와 같은 구성으로 서비스에서 실제 사용중이신가요?
Lambda의 coldsatrt로 인한 답답함이 느껴지진 않으신가요?