In this article, we will look at how to create a securely authenticated serverless NodeJs API leveraging on AWS API Gateway key and custom JWT token. AWS SAM (Serverless Application Model) creates secure, high-performing APIs and provides developers with a simplified development environment that allows them to solely focus on writing code without worrying about server management, infrastructure scaling, or hardware failures.
Designs
High-Level Architecture Diagram
- The API calls from a mobile or web client are routed through an API Gateway to a Lambda function for processing. Data is persisted in a DynamoDb and logs stored on CloudWatch.
Sequence Diagram
- The user first creates an account and then login using their credentials i.e. username and a password to get an access token. Making use of the access token, the user is then able to create a new event, update details and view all the events available.
Prerequisite
- Basic skill in NodeJs
- AWS Account
- AWS CLI
Follow this guide (AWS CLI) to install.
$ aws --version
aws-cli/2.7.32 Python/3.9.11 Darwin/23.0.0 exe/x86_64 prompt/off
- SAM CLI
Follow this guide (SAM CLI) to install.
$ sam --version
SAM CLI, version 1.73.0
Source
You can get the full source code here.
$ git clone https://github.com/bensonmacharia/sam-node-api.git
Let's Build
1. Environment Setup
- Create the project folder and initialise the sam project.
$ mkdir sam-projects && cd sam-projects
$ sam init -r nodejs18.x -d npm -n sam-node-api
Which template source would you like to use?
Choice: 1
Choose an AWS Quick Start application template
Template: 6
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: N
Would you like to enable monitoring using CloudWatch Application Insights? [y/N]: N
- We are specifying to use nodejs version 18, npm as the dependency manager for our Lambda runtime and sam-node-api as the name of our project. We have also chosen not to enable X-Ray tracing and CloudWatch Application Insights.
2. Let's do some clean up
- Open the generated source code in your favourite IDE. In this case, vscode.
$ cd sam-node-api && code .
- Delete the files that we will not be using.
$ rm -rf sam-node-dev events && rm -rf __tests__
3. Time now to edit the template.yaml file
In AWS SAM,
template.yaml
serves as the foundational configuration file for defining our serverless application. Clear the content on the template.yaml file and add the following below content.-
AWSTemplateFormatVersion
: Specifies the AWS CloudFormation template version. For SAM templates, this is usually set to '2010-09-09'.AWSTemplateFormatVersion: 2010-09-09
-
Transform
: Specifies the macro (transform) that AWS CloudFormation uses to process the template. For SAM templates, this should be set to 'AWS::Serverless-2016-10-31'.- AWS::Serverless-2016-10-31
-
Description
: Provides a description of the CloudFormation stack and the serverless application.Description: >- sam-node-api
Resources
: This section defines the AWS resources that make up your serverless application, such as AWS Lambda functions, API Gateway APIs, DynamoDB tables, and more. In this case we have an API Gateway, Secrets Manager, Lambda functions and DynamoDB as below.
API Gateway
Resources:
# Create an API Gateway and add an API Key
ApiGatewayEndpoint:
Type: 'AWS::Serverless::Api'
Properties:
StageName: Prod
Auth:
# Require API Key for all endpoints
ApiKeyRequired: true
UsagePlan:
CreateUsagePlan: PER_API
UsagePlanName: GatewayAuthorization
Secret Manager
JWTUserTokenSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: JWTUserTokenSecret
Description: "This secret has a dynamically generated secret password."
# Generate a random string 30 charatcers long for the JWT secret
GenerateSecretString:
GenerateStringKey: 'jwt_secret'
PasswordLength: 30
ExcludeCharacters: '"@/\:;+*'''
SecretStringTemplate: '{"secret_name": "sam-node-app-jwt-secret"}'
Get All Events Lambda Function
# This is a Lambda function config associated with the source code: get-all-events.js for getting all the Events.
getAlleventsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/get-all-events.getAllEventsHandler
Runtime: nodejs18.x
Architectures:
- x86_64
MemorySize: 128
Timeout: 100
Description: Includes a HTTP get method to get all events from a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the EventsTable
- DynamoDBCrudPolicy:
TableName: !Ref EventsTable
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Ref JWTUserTokenSecret
Environment:
Variables:
# Make table name accessible as environment variable from function code during execution
SAMPLE_TABLE: !Ref EventsTable
Events:
Api:
Type: Api
Properties:
RestApiId:
Ref: ApiGatewayEndpoint
# Expose the API through the path /v1/api/events
Path: /v1/api/events
Method: GET
Get Single Event By ID Lambda Function
# This is a Lambda function config associated with the source code: get-event.js for getting a single Event by its ID
getEventByIdFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/get-event.getEventByIdHandler
Runtime: nodejs18.x
Architectures:
- x86_64
MemorySize: 128
Timeout: 100
Description: Includes a HTTP get method to get one event by id from a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the EventsTable
- DynamoDBCrudPolicy:
TableName: !Ref EventsTable
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Ref JWTUserTokenSecret
Environment:
Variables:
# Make table name accessible as environment variable from function code during execution
SAMPLE_TABLE: !Ref EventsTable
Events:
Api:
Type: Api
Properties:
RestApiId:
Ref: ApiGatewayEndpoint
# Expose the API through the path /v1/api/event/<id>
Path: /v1/api/event/{id}
Method: GET
Create an Event Lambda Function
# This is a Lambda function config associated with the source code: put-item.js for posting an Event into the database
addEventFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/add-event.addEventHandler
Runtime: nodejs18.x
Architectures:
- x86_64
MemorySize: 128
Timeout: 100
Description: Includes a HTTP post method to add one event to a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the EventsTable
- DynamoDBCrudPolicy:
TableName: !Ref EventsTable
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Ref JWTUserTokenSecret
Environment:
Variables:
# Make table name accessible as environment variable from function code during execution
SAMPLE_TABLE: !Ref EventsTable
Events:
Api:
Type: Api
Properties:
RestApiId:
Ref: ApiGatewayEndpoint
# Expose the API through the path /v1/api/event
Path: /v1/api/event
Method: POST
User Registration Lambda Function
# This is a Lambda function config associated with the source code: user-register.js for adding a new user record into the database
registerUserFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/user-register.registerUserHandler
Runtime: nodejs18.x
Architectures:
- x86_64
MemorySize: 128
Timeout: 100
Description: A HTTP post method to register a user and add record to a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the UsersTable
- DynamoDBCrudPolicy:
TableName: !Ref UsersTable
Environment:
Variables:
# Make table name accessible as environment variable from function code during execution
USER_TABLE: !Ref UsersTable
Events:
Api:
Type: Api
Properties:
RestApiId:
Ref: ApiGatewayEndpoint
# Expose the API through the path /v1/auth/user/register
Path: /v1/auth/user/register
Method: POST
Auth:
ApiKeyRequired: false
User Login Lambda Function
# This is a Lambda function config associated with the source code: user-login.js for fetching a user record from the database
loginUserFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/user-login.loginUserHandler
Runtime: nodejs18.x
Architectures:
- x86_64
MemorySize: 128
Timeout: 100
Description: A HTTP post method to login a user and fetch record from a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the UsersTable
- DynamoDBCrudPolicy:
TableName: !Ref UsersTable
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Ref JWTUserTokenSecret
Environment:
Variables:
# Make table name accessible as environment variable from function code during execution
USER_TABLE: !Ref UsersTable
Events:
Api:
Type: Api
Properties:
RestApiId:
Ref: ApiGatewayEndpoint
# Expose the API through the path /v1/auth/user/login
Path: /v1/auth/user/login
Method: POST
Auth:
ApiKeyRequired: false
Events DynamoDB Table
# DynamoDB table to store Event details
EventsTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2
Users DynamoDB Table
# DynamoDB table to store User details
UsersTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: username
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2
-
Outputs
: Specifies the values to be exported from the stack once it's created. Outputs can include endpoint URLs, resource IDs, or any other information you want to make accessible after the deployment.Outputs: ApiGateway: Description: "The URL is:" Value: !Sub "https://${ApiGatewayEndpoint}.execute-api.${AWS::Region}.amazonaws.com/Prod/" ApiKey: Description: "You can find your API Key in the AWS console: (Put in the request HEADER as 'x-api-key')" Value: !Sub "https://console.aws.amazon.com/apigateway/home?region=${AWS::Region}#/api-keys/${ApiGatewayEndpointApiKey}"
4. Edit the Environment Variables
- Modify the
env.json
file to define our variables for the database names as below.{ "getAllEventsFunction": { "SAMPLE_TABLE": "EventsTable" }, "getEventByIdFunction": { "SAMPLE_TABLE": "EventsTable" }, "addEventFunction": { "SAMPLE_TABLE": "EventsTable" }, "registerUserFunction": { "USER_TABLE": "UsersTable" }, "loginUserFunction": { "USER_TABLE": "UsersTable" } }
5. Install Dependencies
# Install API Gateway SDK
$ npm install @aws-sdk/client-api-gateway
# Install Secrets Manager SDK
$ npm install @aws-sdk/client-secrets-manager
# Install jsonwebtoken package for generating JWT tokens
$ npm install jsonwebtoken
# Install bcryptjs package for password encryption
$ npm install bcryptjs
# Install uuid package for generating uuidv4 ids
$ npm install uuid
6. Let's Handle User Registration
-
Create a new file
user-register.mjs
undersam-node-api/src/handlers
$ touch src/handlers/user-register.mjs
-
Add the user registration logic on
user-register.mjs
// Import bcrypt for encrypting user password import bcrypt from 'bcryptjs'; // Import uuid for generating unique user ID import { v4 as uuidv4 } from 'uuid';
// Create a DocumentClient that represents the query to add an item
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);
// Get the DynamoDB table name from environment variables
const tableName = process.env.USER_TABLE;
/**
-
A HTTP post method to add one user to a DynamoDB table.
*/
export const registerUserHandler = async (event) => {
if (event.httpMethod !== 'POST') {
throw new Error(postMethod only accepts POST method, you tried: ${event.httpMethod} method.
);
}
// All log statements are written to CloudWatch
console.info('received:', event);// Get username and password from the body of the request
const body = JSON.parse(event.body);
// Use a random uuidv4 string as a User ID
const id = uuidv4();const username = body.username;
// Generate a hashed password string
const password = bcrypt.hashSync(body.password, 8);// Creates a new user, or replaces an old user record with a new one
var params = {
TableName: tableName,
Item: { id: id, username: username, password: password }
};try {
const data = await ddbDocClient.send(new PutCommand(params));
console.log("Success - user registered or updated", data);
} catch (err) {
console.log("Error", err.stack);
}const responseData = {
message: "Registration successful. Proceed to login",
username: body.username}
const response = {
statusCode: 201,
body: JSON.stringify(responseData)
};// All log statements are written to CloudWatch
console.info(response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}
);
return response;
};
**7. Handle User Login**
- Create a new file `user-login.mjs` under `sam-node-api/src/handlers`
$ touch src/handlers/user-login.mjs
- Add the user login logic on `user-login.mjs`
// Import bcrypt for encrypting user password and comparing the password hash
import bcrypt from 'bcryptjs';
// Create a DocumentClient that represents the query to add an item
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb';
import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
// Import jwt for generating the user token
import jwt from 'jsonwebtoken';
const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);
const clientsecret = new SecretsManagerClient();
// Get the DynamoDB table name from environment variables
const tableName = process.env.USER_TABLE;
/**
-
A HTTP post method for user login.
*/
export const loginUserHandler = async (event) => {
if (event.httpMethod !== 'POST') {
throw new Error(postMethod only accepts POST method, you tried: ${event.httpMethod} method.
);
}
// All log statements are written to CloudWatch
console.info('received:', event);// Get username and password from the body of the request
const body = JSON.parse(event.body);
const username = body.username;
const password = body.password;// Fetch the JWT secret string from AWS Secrets Manager
const secret_value = await clientsecret.send(new GetSecretValueCommand({
SecretId: "JWTUserTokenSecret",
}));
const jwt_secret = JSON.parse(secret_value.SecretString);var params = {
TableName: tableName,
Key: { username: username },
};var token = "";
var message = "";
var status_code = 200;try {
const data = await ddbDocClient.send(new GetCommand(params));
var item = data.Item;
// Comprate a hash of the received password from the request body with the password hash from the database, if they match login the user and generate a JWT token
if (bcrypt.compareSync(password, item.password)) {
// create JWT token
const jwttoken = jwt.sign(
{
userId: item.id,
userName: body.username,
},
jwt_secret.jwt_secret,
{ expiresIn: "1h" }
);
message = "Login successful.";
token = jwttoken;
} else {
status_code = 403;
message = "Wrong password. Try again";
token = "NOT OKAY";
}
} catch (err) {
console.log("Error", err);
}const responseData = {
message: message,
user: {
username: body.username,
token: token
}
}const response = {
statusCode: status_code,
body: JSON.stringify(responseData)
};// All log statements are written to CloudWatch
console.info(response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}
);
return response;
};
8. Add Events Logic
-
Rename the file
put-item.mjs
insam-node-api/src/handlers
toadd-event.mjs
$ mv src/handlers/put-item.mjs src/handlers/add-event.mjs
-
Edit the
add-event.mjs
file// Create a DocumentClient that represents the query to add an item import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken';
const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);
const clientsecret = new SecretsManagerClient();
// Get the DynamoDB table name from environment variables
const tableName = process.env.SAMPLE_TABLE;
/**
-
A HTTP post method to add one Event item to a DynamoDB table.
*/
export const addEventHandler = async (event) => {
if (event.httpMethod !== 'POST') {
throw new Error(postMethod only accepts POST method, you tried: ${event.httpMethod} method.
);
}
// All log statements are written to CloudWatch
console.info('received:', event);// Get token from the authorization header
const token = await event.headers.Authorization.split(" ")[1];
if (!token) {
throw new Error(Authorization token required.
);
}// Get Event id, title and description from the body of the request
const body = JSON.parse(event.body);
const id = body.id;
const title = body.title;
const description = body.description;// Fetch the JWT secret string from AWS Secrets Manager
const secret_value = await clientsecret.send(new GetSecretValueCommand({
SecretId: "JWTUserTokenSecret",
}));
const jwt_secret = JSON.parse(secret_value.SecretString);//Check if the token is valid
const decodedToken = jwt.verify(token, jwt_secret.jwt_secret);
if (!decodedToken) {
throw new Error(Authorization token invalid or it has expired.
);
}// Decode the JWT token to get user data
const userID = decodedToken.userId;
const userName = decodedToken.userName;console.info('decode-username:', userName);
console.info('decode-userid:', userID);// Creates a new Event item, or replaces an old item with a new item
var params = {
TableName : tableName,
Item: { id : id, title: title, description: description, added_by: userName, added_by_id: userID}
};try {
const data = await ddbDocClient.send(new PutCommand(params));
console.log("Success - event added successfully", data);
} catch (err) {
console.log("Error", err.stack);
}const response = {
statusCode: 200,
body: JSON.stringify(body)
};// All log statements are written to CloudWatch
console.info(response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}
);
return response;
};
9. View All Events Login
-
Rename the file
get-all-items.mjs
insam-node-api/src/handlers
toget-all-events.mjs
$ mv src/handlers/get-all-items.mjs src/handlers/get-all-events.mjs
-
Edit the
get-all-events.mjs
fileimport { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken';
const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);
const clientsecret = new SecretsManagerClient();
// Get the DynamoDB table name from environment variables
const tableName = process.env.SAMPLE_TABLE;
/**
-
A HTTP get method to get all events from a DynamoDB table.
*/
export const getAllEventsHandler = async (event) => {
if (event.httpMethod !== 'GET') {
throw new Error(getAllItems only accept GET method, you tried: ${event.httpMethod}
);
}
// All log statements are written to CloudWatch
console.info('received:', event);// Get token from the authorization header
const token = await event.headers.Authorization.split(" ")[1];
if (!token) {
throw new Error(Authorization token required.
);
}// Fetch the JWT secret string from AWS Secrets Manager
const secret_value = await clientsecret.send(new GetSecretValueCommand({
SecretId: "JWTUserTokenSecret",
}));
const jwt_secret = JSON.parse(secret_value.SecretString);//Check if the token is valid
const decodedToken = jwt.verify(token, jwt_secret.jwt_secret);
if (!decodedToken) {
throw new Error(Authorization token invalid or it has expired.
);
}// Get all items from the table (only first 1MB data, you can use
LastEvaluatedKey
to get the rest of data)
var params = {
TableName : tableName
};try {
const data = await ddbDocClient.send(new ScanCommand(params));
var items = data.Items;
} catch (err) {
console.log("Error", err);
}const response = {
statusCode: 200,
body: JSON.stringify(items)
};// All log statements are written to CloudWatch
console.info(response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}
);
return response;
}
10. View Event By ID Login
-
Rename the file
get-by-id.mjs
insam-node-api/src/handlers
toget-event.mjs
$ mv src/handlers/get-by-id.mjs src/handlers/get-event.mjs
-
Edit the
get-event.mjs
fileimport { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; // Import jwt for validating the user token import jwt from 'jsonwebtoken';
const client = new DynamoDBClient({});
const ddbDocClient = DynamoDBDocumentClient.from(client);
const clientsecret = new SecretsManagerClient();
// Get the DynamoDB table name from environment variables
const tableName = process.env.SAMPLE_TABLE;
/**
- A HTTP get method to get one Event item by id from a DynamoDB table.
*/
export const getEventByIdHandler = async (event) => {
if (event.httpMethod !== 'GET') {
throw new Error(
getMethod only accept GET method, you tried: ${event.httpMethod}
); } // All log statements are written to CloudWatch console.info('received:', event);
// Get the token from the authorization header
const token = await event.headers.Authorization.split(" ")[1];
if (!token) {
throw new Error(Authorization token required.
);
}
// Fetch the JWT secret string from AWS Secrets Manager
const secret_value = await clientsecret.send(new GetSecretValueCommand({
SecretId: "JWTUserTokenSecret",
}));
const jwt_secret = JSON.parse(secret_value.SecretString);
//Check if the token is valid
const decodedToken = jwt.verify(token, jwt_secret.jwt_secret);
if (!decodedToken) {
throw new Error(Authorization token invalid or it has expired.
);
}
// Get id from pathParameters from APIGateway because of /{id}
at template.yaml
const id = event.pathParameters.id;
// Get the item from the table
var params = {
TableName : tableName,
Key: { id: id },
};
var scode = 200;
var bdy = "";
try {
const data = await ddbDocClient.send(new GetCommand(params));
var item = data.Item;
bdy = JSON.stringify(item);
if (!bdy) {
scode = 404;
bdy = JSON.stringify("Event details not found.");
}
} catch (err) {
console.log("Error", err);
scode = 400;
bdy = err;
}
const response = {
statusCode: scode,
body: bdy
};
// All log statements are written to CloudWatch
console.info(response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}
);
return response;
}
**11. Build and Deploy the API**
- Build the SAM App
$ sam build
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
- Deploy the SAM App
$ sam deploy --guided
Stack Name [sam-app]: sam-node-app
AWS Region [us-east-1]: us-east-1
Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: y
SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: Y
Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: N
Save arguments to configuration file [Y/n]: Y
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
Deploy this changeset? [y/N]: y
CloudFormation outputs from deployed stack
Outputs
Key ApiKey
Description You can find your API Key in the AWS console: (Put in the request HEADER as 'x-api-key')
Value https://console.aws.amazon.com/apigateway/home?region=us-east-1#/api-keys/w22y9chd81
Key ApiGateway
Description The URL is:
Value https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/
Successfully created/updated stack - sam-node-app in us-east-1
**12. Test the API**
- Test user registration
$ curl -X POST --header "Content-Type: application/json" --data '{"username":"bmacharia", "password":"pAssW0rd@s3cr3t"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/auth/user/register
HTTP/1.1 200 OK
{"message":"Registration successful. Proceed to login","username":"bmacharia"}%
- Test user login
$ curl -X POST --header "Content-Type: application/json" --data '{"username":"bmacharia", "password":"pAssW0rd@s3cr3t"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/auth/user/login
HTTP/1.1 200 OK
{"message":"Login successful.","user":{"username":"bmacharia","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY"}}%
- Test creating event
$ curl -X POST --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" --data '{"id":"2", "title":"Cybersecurity Summit", "description":"Downtown Hotel, 31st December 2023"}' https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/event
HTTP/1.1 200 OK
{"id":"2","title":"Cybersecurity Summit","description":"Downtown Hotel, 31st December 2023"}%
- Test getting all events
$ curl --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/events
HTTP/1.1 200 OK
[{"description":"Downtown Hotel, 31st December 2023","id":"2","title":"Cybersecurity Summit","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"},{"description":"Whitesands Hotel, 1st January 2024","id":"1","title":"GRC Conference","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"}]%
- Get Event By ID
$ curl --header "Content-Type: application/json" --header "x-api-key: Laqoh0N6kCao2yw4SeDxCa9pwRdjxwra267VGjMH" --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4NDMxMTU5Ny05YTlkLTQ2NDEtYjBjMS1mYTUzMGIzNTkwNzYiLCJ1c2VyTmFtZSI6ImJtYWNoYXJpYSIsImlhdCI6MTY5ODk2MjM2OSwiZXhwIjoxNjk4OTY1OTY5fQ.keoma2P6fdXnkrRklWX1vVoyaUXB_G06p6gvQLyOHHY" https://d2io9v58l4.execute-api.us-east-1.amazonaws.com/Prod/v1/api/event/1
HTTP/1.1 200 OK
{"description":"Whitesands Hotel, 1st January 2024","id":"1","title":"GRC Conference","added_by":"bmacharia","added_by_id":"84311597-9a9d-4641-b0c1-fa530b359076"}%
**13. Clean Up**
- Delete the SAM App
$ sam delete --stack-name sam-node-app
Are you sure you want to delete the stack sam-node-app in the region us-east-1 ? [y/N]: y
Are you sure you want to delete the folder sam-node-app in S3 which contains the artifacts? [y/N]: y
Deleted successfully
Top comments (1)
Much better and useful than mostly all official docs :)