Recently, I've been working with AWS in order to experience how it is to build a MVP really quickly. The goal I've been trying to achieve is to use as much AWS tools as possible to get a working product in production the fastest.
To do that, I choose to rely on :
Once Cognito configured and a user created, I've been able to create a login form quite easily following the server-stack tutorial.
Then, I've started protecting my API-Gateway endpoints with an IAM role in order to make some of them only reachable for logged users.
functions:
addSnippet:
handler: bin/handlers/add_snippet
package:
include:
- ./bin/handlers/add_snippet
events:
- http:
path: snippets
method: post
cors: true
authorizer: aws_iam
By doing this, I needed to start making my http calls authenticated… this is where things started to mess.
Make an authenticated call when working with Cognito and API-Gateway means using a JWT retrieved from the authorizer (the IAM role here). But this token must be signed.
AWS used to recommend their Javascript SDK to do that. But the procedure is quite complicated. We need to use at least 3 different libs to do the job (here is an example). To fix that and make things easier for developers, Amazon shipped a React lib called aws-amplify
: https://github.com/aws/aws-amplify.
Amplify may be seen as a unification of those 3 libs (including the AWS JS SDK) thought to make developer life easier.
And indeed, here is how it works :
Configuration
Amplify.configure({
Auth: {
mandatorySignIn: true,
region: config.cognito.REGION,
userPoolId: config.cognito.USER_POOL_ID,
identityPoolId: config.cognito.IDENTITY_POOL_ID,
userPoolWebClientId: config.cognito.APP_CLIENT_ID
},
API: {
endpoints: [
{
name: "snippets",
endpoint: config.apiGateway.URL,
region: config.apiGateway.REGION
},
]
}
});
Login
Auth.signIn(email, password);
Once logged in, you can easily call your API endpoints using :
API.post("snippets","snippets/", {
body: data,
});
Simple, right? You can find more detailed examples in the AWS Amplify API documentation.
About getting a working product as soon as possible, there is a thing you probably don't want to do by yourself : payload validation.
Good news, API-Gateway provide a JSON schema validation feature !
Documentation can be found here : https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html
The big picture is :
{
"message": "Missing required request parameters: [q1]"
}
Well, let's put it together. What if I want to use those validation error messages in my front-end and display them to the user?
Under the hood, ws-amplify
use Axios to make http calls.
It means than, contrary to Fetch
, not only network errors are thrown as an exception. With Axios
, all > 299 response status code are thrown as an exception.
Here are 2 examples of use.
With Fetch :
async function postSnippet(data) {
try {
const response = await fetch(`http://example.com/snippets/`, {
method: 'POST',
body: JSON.stringify(data),
});
if (!response.ok) {
throw response;
}
return await response.json();
} catch (err) {
console.log(await err.json())
}
}
With Amplify :
async function postSnippet(data) {
try {
return await API.post("snippets","snippets/", {
body: data,
});
} catch (err) {
console.log(err.response)
}
}
See the difference?
Because Amplify use Axios, when API-Gateway returns a 400 status code, the error is thrown and if you want to use the returned payload you need to access the err.response.data
object (see this issue). But this is not so obvious and more over, there is no example of error handling in the aws-amplify
documentation.
I have opened a Pull Request (https://github.com/aws/aws-amplify/pull/633) to document this behaviour.
I also answer my own question on stackoverflow to make things clearer : https://stackoverflow.com/questions/49633463/how-to-handle-api-errors-using-aws-amplify