serverless-haskell-0.12.2: Deploying Haskell code onto AWS Lambda using Serverless

Stabilityexperimental
PortabilityPOSIX
Safe HaskellNone
LanguageHaskell2010

AWSLambda

Description

Tools for running Haskell on AWS Lambda using Serverless.

Usage

To deploy a Haskell function on AWS Lambda:

  • Initialise a Serverless project in the same directory as your Stack-enabled package and install the serverless-haskell plugin:

    npm init -y
    npm install --save serverless serverless-haskell@x.y.z

    The version of the NPM package to install must match the version of the Haskell package.

  • Create serverless.yml with the following contents:

    service: myservice
    
    provider:
      name: aws
      runtime: haskell
    
    functions:
      myfunc:
        handler: mypackage.mypackage-exe
        # Here, mypackage is the Haskell package name and mypackage-exe is the
        # executable name as defined in the Cabal file
    
    plugins:
      - serverless-haskell
  • Write your main function using lambdaMain.
  • Add aeson and serverless-haskell to package.yaml:
dependencies:
- base >= 4.7 && < 5
- aeson
- serverless-haskell
  • Build and test locally using sls invoke local:

The serverless-haskell plugin will build the package using Stack. Note that the first build can take a long time. Consider adding export SLS_DEBUG=* so you can see what is happening.

export SLS_DEBUG=*
sls invoke local -f myfunc
  • Use sls deploy to deploy the executable to AWS Lambda.

    The serverless-haskell plugin will build the package using Stack and upload it to AWS together with a JavaScript wrapper to pass the input and output from/to AWS Lambda.

    export SLS_DEBUG=*
    sls deploy

    You can test the function and see the invocation results with sls invoke -f myfunc.

API Gateway

This plugin supports handling API Gateway requests. Declare the HTTP events normally in serverless.yml and use APIGateway in the handler to process them.

Serverless Offline can be used for local testing of API Gateway requests. You must use --useDocker flag so that the native Haskell runtime works correctly.

Additional features

Configuration options are passed to the plugin under haskell key in custom section of serverless.yml.

  • To add flags to stack build, specify them as an array under stackBuildArgs:

    custom:
      haskell:
        stackBuildArgs:
          - --pedantic
          - --allow-different-user
  • Dependent system libraries not present in the AWS Lambda environment will be automatically uploaded along with the executable. Note that while statically linking the executable via Cabal options is possible, it might still require the corresponding glibc version on the AWS environment.
  • Stack's Docker image will be used to match the AWS Lambda Linux environment. To disable this, set docker key to false, but beware that the resulting binary might not have the required libraries to run on Lambda.

    custom:
      haskell:
        docker: false
  • By default, stack build command is invoked to build all the project's executables. To only build the ones used in the handlers, set buildAll key to false. Note that at least Stack 1.9.3 has better caching behavior when building the whole project, as it doesn't need to reconfigure the build for the individual ones every time.

    custom:
      haskell:
        buildAll: false
Synopsis

Documentation

lambdaMain Source #

Arguments

:: (FromJSON event, ToJSON res, MonadCatch m, MonadIO m) 
=> (event -> m res)

Function to process the event

-> m () 

Process incoming events from serverless-haskell using a provided function.

The handler receives the input event given to the AWS Lambda function, and its return value is returned from the function.

This is intended to be used as main, for example:

import qualified Data.Aeson as Aeson

import AWSLambda

main = lambdaMain handler

handler :: Aeson.Value -> IO [Int]
handler evt = do
  putStrLn "This should go to logs"
  print evt
  pure [1, 2, 3]

The handler function can receive arbitrary JSON values from custom invocations, or one of the events from the AWSLambda.Events module, such as S3Event:

import AWSLambda.Events.S3Event

handler :: S3Event -> IO ()
handler evt = do
  print $ records evt

If the Lambda function needs to process several types of events, use Alternative to combine several handlers:

import AWSLambda
import AWSLambda.Events.S3Event
import Data.Aeson
import Data.Aeson.Alternative

main = lambdaMain $ handlerS3 `alternative` handlerCustom

handlerS3 :: S3Event -> IO ()
handlerS3 = _

handlerCustom :: Value -> IO ()
handlerCustom = _

When run outside the AWS Lambda environment, the input is read as JSON from the command line, and the result of the execution is printed, also as JSON, to the standard output.