LoginSignup
0
0

More than 3 years have passed since last update.

URL短縮サービスを API Gateway + Lambda で

Posted at

概要

URL短縮サービスを AWS API Gateway + Lambda で実装した例になります。
データストアには DynamoDB を使用しています。
掲載しているコードはこちらでも公開しています。

セットアップ

デプロイにはServerlessフレームワークを使うのでなければインストールします。
serverless-hooks-pluginはデプロイコマンドをフックするためのプラグインです。

mkdir simple-url-shortener && cd simple-url-shortener
npm install -g serverless
npm install serverless-hooks-plugin

以下で使用するライブラリのGemfileを作っておきます。

Gemfile
gem 'aws-record'
gem 'hashids'

ハンドラーの作成

今回作成するルートは2です。
1つはURLを登録して短縮URLにして返す/url/shortener
もう一つは短縮URLへアクセスした時に登録したURLへリダイレクトさせる/です。

短縮URLモデル

URLの保存先にはDynamoDBを使います。
aws-recordというDynamoDBのAPIラッパーを使って短縮URLを表すモデルを作成します。

models/url_shortener.rb
require 'aws-record'

class UrlShortener
  include Aws::Record

  set_table_name ENV['DYNAMODB_TABLE']

  string_attr :id, hash_key: true
  string_attr :short_url
  string_attr :long_url
  string_attr :date

  # Check url valid
  def valid?
    url = begin
            URI.parse(long_url)
          rescue StandardError
            false
          end
    url.is_a?(URI::HTTP) || url.is_a?(URI::HTTPS)
  end
end

URLを登録して短縮URLを生成

ランダムなIDの生成にはHashidsというライブラリを使っています。

routes/url.rb
require_relative '../models/url_shortener'
require 'aws-record'
require 'hashids'

def handler(event:, context:)
  data = JSON.parse(event['body'])

  # Check whether longUrl is valid
  url = UrlShortener.new
  url.long_url = data['longUrl']

  return { statusCode: 401, body: 'Invalid long url' } unless url.valid?

  # Create url code
  now = Time.now

  hashids = Hashids.new(ENV['HASHIDS_SALT'])
  hash = hashids.encode(now.to_i, now.usec)

  base_url = ENV['BASE_URL'] || "https://#{event['headers']['Host']}/#{event['requestContext']['stage']}"
  url.id = hash
  url.short_url = "#{base_url}/#{url.id}"
  url.date = now.to_s

  begin
    url.save
  rescue Aws::Record::Errors::RecordError => e
    puts e
    { statusCode: 500, body: e }
  end

  { statusCode: 200, body: url.short_url }
end

短縮URLへアクセスしたら登録URLへリダイレクト

routes/index.rb
require_relative '../models/url_shortener'

def handler(event:, context:)
  code = event['pathParameters']['code']
  begin
    url = UrlShortener.find(id: code)
  rescue Aws::Record::Errors::NotFound => e
    puts e
    { statusCode: 404, body: 'URL not found' }
  rescue Aws::Record::Errors::RecordError => e
    puts e
    { statusCode: 500, body: 'Internal server error' }
  end

  { statusCode: 301, headers: { Location: url.long_url } }
end

デプロイ

serverless.yml を作成してデプロイコマンドを実行します。

serverless.yml
service: simple-url-shortener

provider:
  name: aws
  region: ${opt:region, 'us-east-1'}
  runtime: ruby2.5
  environment:
    DYNAMODB_TABLE: url-shortener-${opt:stage, 'dev'}
    HASHIDS_SALT: 'This is my salt'
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: 'arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}'

plugins:
  - serverless-hooks-plugin

custom:
  hooks:
    package:initialize:
      - bundle install --deployment

functions:
  redirectOriginal:
    handler: routes/index.handler
    events:
      - http:
          path: /{code}
          method: get
  shortener:
    handler: routes/url.handler
    events:
      - http:
          path: url/shortener
          method: post
          cors: true

resources:
  Resources:
    UrlShortenerTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

これで準備が整ったのでいよいよデプロイです。

bundle
sls deploy

テスト

デプロイしたAPIをテストしてみます。
返って来たURLをブラウザで開いてみてhttps://example.comに飛んでいれば成功です。

curl -d '{"longUrl":"https://example.com"}' https://{api}.execute-api.{region}.amazonaws.com/url/shortener

さいごに

今回作成したAWSのリソースは以下のコマンドで削除できます。

sls remove
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0