Skip to content

Commit dd4deef

Browse files
committed
Documentation updates.
1 parent cd15f21 commit dd4deef

5 files changed

+47
-151
lines changed

doc/01-getting-started.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ packages:
4949
- .
5050

5151
extra-deps:
52-
- aws-lambda-haskell-runtime-2.0.1
52+
- aws-lambda-haskell-runtime-3.0.0
5353
```
5454
5555
## Adding the dependency to an existing project
@@ -60,15 +60,15 @@ to the `stack.yaml` file:
6060

6161
```yaml
6262
extra-deps:
63-
- aws-lambda-haskell-runtime-2.0.1
63+
- aws-lambda-haskell-runtime-3.0.0
6464
```
6565

6666
and, to the `package.yaml` file:
6767

6868
```yaml
6969
dependencies:
7070
- ... # other dependencies of your project
71-
- aws-lambda-haskell-runtime >= 2.0
71+
- aws-lambda-haskell-runtime >= 3.0.0
7272
```
7373

7474
## Keep reading!

doc/02-adding-a-handler.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ modules:
2121
```haskell
2222
{-# LANGUAGE DeriveGeneric #-}
2323
{-# LANGUAGE DeriveAnyClass #-}
24+
2425
module Lib where
2526
```
2627

@@ -46,7 +47,7 @@ Now, let's write the handler. It **must** be a function that is called `handler`
4647
The arguments to this function will always go like this:
4748

4849
* The first argument to this handler will always be the input type we expect (note that it has to implement `FromJSON`).
49-
* The second argument is the `Aws.Lambda` `Context` type, which has some information regarding our Lambda execution.
50+
* The second argument is the `Aws.Lambda` `Context` type, which has some information regarding our Lambda execution. The `Context` type also takes a `context` parameter, which we can use if we want to have some state that is shared between Lambda calls. For this example, we don't want to have such state, so we'll just use `()`.
5051

5152
The output will always be an `IO (Either errorType resultType)` where
5253

@@ -59,7 +60,7 @@ For example, here we will check if the age of a `Person` is positive, and will r
5960
will return a `String` error:
6061

6162
```haskell top
62-
handler :: Person -> Context -> IO (Either String Person)
63+
handler :: Person -> Context () -> IO (Either String Person)
6364
handler person context =
6465
if age person > 0 then
6566
pure (Right person)

doc/03-configuring-the-dispatcher.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,28 @@ The dispatcher is a special main function that checks which handler it has to ru
88
and runs it
99

1010
To make the package generate a dispatcher for you, you have to configure it in
11-
your `app/Main.hs` file.
11+
your `app/Main.hs` file to use the `generateLambdaDispatcher` function from `Aws.Lambda`.
1212

13-
First, activate `TemplateHaskell` for enabling the metaprogramming features of Haskell.
13+
The `generateLambdaDispatcher` function has two options: `StandaloneLambda` and `UseWithAPIGateway`.
14+
15+
Use `UseWithAPIGateway` when you'll be exposing your lambda through API Gateway. For more information check the [Use with API Gateway](./04-usage-with-api-gateway.md) page.
16+
17+
Use `StandaloneLambda` when you want to use your lambda as usual. This is what we're going to use for our person age validator function.
18+
19+
To continue the person age validator lambda, activate `TemplateHaskell` for enabling the metaprogramming features of Haskell.
1420
Then, import the `Aws.Lambda` module:
1521

1622
```haskell
1723
{-# LANGUAGE TemplateHaskell #-}
24+
1825
module Main where
1926
import Aws.Lambda
2027
```
2128

2229
After this, add a line with the following statement:
2330

2431
```haskell
25-
generateLambdaDispatcher
32+
generateLambdaDispatcher StandaloneLambda defaultDispatcherOptions
2633
```
2734

2835
This statement will generate something that looks a little bit like this:

doc/04-usage-with-api-gateway.md

Lines changed: 25 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -4,158 +4,42 @@ title: Usage with API Gateway
44

55
# Usage with API Gateway
66

7-
In order to write a handler for the API Gateway, we will receive a special JSON with
8-
a lot of fields with information from the API Gateway, and we will have to return
9-
a special JSON as the response.
7+
A common use-case is to expose your lambdas through API Gateway. Luckily, `aws-lambda-haskell-runtime` has native support for that.
108

11-
## The request
12-
13-
The full request type looks more or less like this:
14-
15-
```haskell
16-
data APIGatewayRequest = APIGatewayRequest
17-
{ resource :: String
18-
, path :: ByteString
19-
, httpMethod :: Method
20-
, headers :: RequestHeaders
21-
, queryStringParameters :: [(ByteString, Maybe ByteString)]
22-
, pathParameters :: HashMap String String
23-
, stageVariables :: HashMap String String
24-
, body :: String
25-
} deriving (Generic, FromJSON)
26-
```
27-
28-
Feel free to copy-paste this data type into your project. Note that you don't have to use all of
29-
it's fields, if you only need, say, the `body` and the `httpMethod` fields, you can use this one
30-
(`resource` is mandatory):
9+
If you're using API Gateway, simply pass the `UseWithAPIGateway` option to `generateLambdaDispatcher` in your `app/Main.hs` file.
3110

3211
```haskell
33-
data APIGatewayRequest = APIGatewayRequest
34-
{ resource :: String
35-
, httpMethod :: Method
36-
, body :: String
37-
} deriving (Generic, FromJSON)
12+
generateLambdaDispatcher UseWithAPIGateway defaultDispatcherOptions
3813
```
3914

40-
## The response
41-
42-
The response type looks more or less like this:
15+
Passing `UseWithAPIGateway` will make the compiler error if you do not have your handlers in the following form:
4316

4417
```haskell
45-
data APIGatewayResponse = APIGatewayResponse
46-
{ statusCode :: Int
47-
, headers :: [(HeaderName, ByteString)]
48-
, body :: String
49-
} deriving (Generic, ToJSON)
18+
handler :: ApiGatewayRequest request -> Context context -> IO (Either (ApiGatewayResponse error) (ApiGatewayResponse success))
19+
handler = ...
5020
```
5121

52-
Again, you can use this type in your project, and use only the fields you need.
53-
Note that `HeaderName` comes from [`Network.HTTP.Simple`](https://hackage.haskell.org/package/http-conduit-2.3.7.1/docs/Network-HTTP-Simple.html#t:Header).
54-
55-
Notice some fields need implementation of ToJSON. See the example with headers below.
56-
57-
## An example
58-
59-
```haskell top
60-
{-# LANGUAGE RecordWildCards #-}
61-
{-# LANGUAGE DuplicateRecordFields #-}
62-
{-# LANGUAGE DeriveAnyClass #-}
63-
{-# LANGUAGE DeriveGeneric #-}
64-
65-
module Lib where
66-
67-
import GHC.Generics
68-
import Aws.Lambda
69-
import Data.Aeson
70-
import qualified Data.ByteString.Lazy.Char8 as ByteString
22+
You can use the `ApiGatewayRequest` wrapper to access additional request information that API Gateway provides, such as path, query string parameters, authentication info and much more. You can find more information about that under the `Input format of a Lambda function for proxy integration` section [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html).
7123

72-
-- Input
73-
data Event = Event
74-
{ resource :: String
75-
, body :: String
76-
} deriving (Generic, FromJSON)
24+
You can use the `ApiGatewayResponse` wrapper in order to return a custom status code or attach custom headers to the response.
7725

78-
-- Output
79-
data Response = Response
80-
{ statusCode:: Int
81-
, body :: String
82-
} deriving (Generic, ToJSON)
26+
Also note the `defaultDispatcherOptions`. They look like this:
8327

84-
-- Type that we decode from the 'body' of 'Event'
85-
data Person = Person
86-
{ name :: String
87-
, age :: Int
88-
} deriving (Generic, FromJSON, ToJSON)
89-
90-
greet :: Person -> String
91-
greet person =
92-
"Hello, " ++ name person ++ "!"
93-
94-
handler :: Event -> Context -> IO (Either String Response)
95-
handler Event{..} context = do
96-
case decode (ByteString.pack body) of
97-
Just person ->
98-
pure $ Right Response
99-
{ statusCode = 200
100-
, body = greet person
101-
}
102-
Nothing ->
103-
pure $ Right Response
104-
{ statusCode = 200
105-
, body = "bad person"
106-
}
107-
```
108-
## An example with headers
109-
```src/Hello.hs```
11028
```haskell
111-
{-# LANGUAGE OverloadedStrings #-}
112-
{-# LANGUAGE FlexibleInstances #-}
113-
{-# LANGUAGE RecordWildCards #-}
114-
{-# LANGUAGE DuplicateRecordFields #-}
115-
{-# LANGUAGE DeriveAnyClass #-}
116-
{-# LANGUAGE DeriveGeneric #-}
117-
118-
module Hello where
119-
120-
import GHC.Generics
121-
import Aws.Lambda
122-
import Data.Aeson
123-
import Network.HTTP.Types.Header
124-
import Data.Text.Encoding
125-
import qualified Data.CaseInsensitive as CI
126-
import qualified Data.Aeson.Types as T
127-
128-
-- Input
129-
data Event = Event
130-
{ resource :: String
131-
, body :: String
132-
} deriving (Generic, FromJSON)
133-
134-
-- Output
135-
data ApiGateWayResponse = ApiGateWayResponse
136-
{ statusCode:: Int
137-
, headers :: ResponseHeaders
138-
, body :: String
139-
} deriving (Generic)
140-
141-
-- function to encode Header
142-
headerToPair :: Header -> T.Pair
143-
headerToPair (cibyte, bstr) = k .= v
144-
where
145-
k = (decodeUtf8 . CI.original) cibyte
146-
v = decodeUtf8 bstr
147-
148-
instance ToJSON ApiGateWayResponse where
149-
toJSON ApiGateWayResponse {..} = object
150-
[ "statusCode" .= statusCode
151-
, "body" .= body
152-
, "headers" .= object (map headerToPair headers)
153-
]
29+
-- | Options that the dispatcher generator expects
30+
newtype DispatcherOptions = DispatcherOptions
31+
{ apiGatewayDispatcherOptions :: ApiGatewayDispatcherOptions
32+
} deriving (Lift)
33+
34+
-- | API Gateway specific dispatcher options
35+
newtype ApiGatewayDispatcherOptions = ApiGatewayDispatcherOptions
36+
{ propagateImpureExceptions :: Bool
37+
-- ^ Should impure exceptions be propagated through the API Gateway interface
38+
} deriving (Lift)
39+
40+
defaultDispatcherOptions :: DispatcherOptions
41+
defaultDispatcherOptions =
42+
DispatcherOptions (ApiGatewayDispatcherOptions True)
43+
```
15444

155-
handler :: Event -> Context -> IO (Either String ApiGateWayResponse)
156-
handler Event {..} _ = pure $ Right ApiGateWayResponse
157-
{ statusCode = 200
158-
, headers = [("Access-Control-Allow-Origin", "*")]
159-
, body = "hello, cors"
160-
}
161-
```
45+
For production environments, you'd likely want to set `propagateImpureExceptions` to `False` in order to prevent your exception messages from being seen.

doc/index.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module WordCount where
2020

2121
import Aws.Lambda
2222

23-
handler :: String -> Context -> IO (Either String Int)
23+
handler :: String -> Context () -> IO (Either String Int)
2424
handler someText _ = do
2525
let wordsCount = length (words someText)
2626
if wordsCount > 0 then
@@ -34,7 +34,11 @@ Then, in the `Main` module:
3434
```haskell
3535
import Aws.Lambda
3636
import qualified WordCount
37-
generateLambdaDispatcher
37+
38+
initializeContext :: IO ()
39+
initializeContext = return ()
40+
41+
generateLambdaDispatcher StandaloneLambda defaultDispatcherOptions
3842
```
3943

4044
# Performance

0 commit comments

Comments
 (0)