Based on tutorial . ToDo app We will do the api points with AWS services for simple to-do app. import axios from 'axios' import shortid from 'shortid' import { FaInfoCircle as Info } from 'react-icons/fa' import { FaTrashAlt as Bin } from 'react-icons/fa' import { Spinner } from '/functions/Spinner' function ToDoApp() { const [toDosState, setToDosState] = useState([]) const [loadingState, setLoadingState] = useState(true) const [updatingState, setUpdatingState] = useState(false) async function getAllToDos() { const res = await axios.get('https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/items') const toDos = res.data.items setToDosState(toDos) setLoadingState(false) } useEffect(() => { getAllToDos() }, []) return ( <div className='toDoContainer'> {loadingState && <div className='center'><Spinner width='30px'/></div> } {toDosState.length === 0 && !loadingState && <div className='center'>Empty</div> } {toDosState.map((toDo, index) => ( <div className='toDoLine' key={toDo.id}> <div className='left'> <Counter num={index + 1}/> <ToDoTxt toDoTxt={toDo.toDoName} toDoId={toDo.id} updatingState={updatingState} setUpdatingState={setUpdatingState}/> </div> <div className='right'> <InfoBtn toDoId={toDo.id} /> <DelBtn toDoId={toDo.id} toDosState={toDosState} setToDosState={setToDosState}/> </div> </div> ))} {!loadingState && <ToDoInput toDosState={toDosState} setToDosState={setToDosState} getAllToDos={getAllToDos}/>} {updatingState && <span className='center'><Spinner height='30px' /></span> } <style jsx>{` .toDoContainer { position: relative; border: 1px dotted lightblue; border-radius: 8px; margin: 10px; padding: 20px; min-height: 100px; } .center { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .toDoLine { display: flex; justify-content: space-between; align-items: center; flex-wrap: nowrap; border-bottom: 1px dotted lightgray; } .left { display: flex; flex-wrap: nowrap; align-items: center; flex-shrink: 1; } .right { display: flex; flex-wrap: nowrap; row-gap: 5px; column-gap: 5px; justify-content: flex-end; } `}</style> </div> ) } function Counter({num}) { return ( <span className='counter'> {num} <style jsx>{` .counter { display: inline-flex; justify-content: center; align-items: center; flex-shrink: 0; background: grey; border-radius: 50%; width: 15px; height: 15px; margin-right: 10px; color: whitesmoke; font-size: 10px; } `}</style> </span> ) } function ToDoTxt({toDoTxt, toDoId, setUpdatingState}) { const [showDetailsState, setShowDetailsState] = useState(false) const [detailsState, setDetailsState] = useState(null) const text = useRef(null) function saveInitText(e) { text.current = e.target.innerText } function didTextChange(e) { const textBefore = text.current const textNow = e.target.innerText if(textBefore !== textNow) updateItem(e) } async function updateItem(e) { setUpdatingState(true) const requestBody = { id: toDoId, updateKey: 'toDoName', updateValue: e.target.innerText, } const res = await axios({ method: 'patch', url: 'https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item', data: requestBody }) const isSuccess = res.data.Message === "SUCCESS" if (!isSuccess) alert('smth wrong') setUpdatingState(false) } return ( <span className='toDoTxt' contentEditable onFocus={saveInitText} onBlur={didTextChange} > {toDoTxt} {showDetailsState && detailsState && <span className='details'>{detailsState}</span>} <style jsx>{` .toDoTxt { color: grey; outline: 0px solid transparent; } `}</style> </span> ) } function InfoBtn({ toDoId }) { const [loadingState, setLoadingState] = useState(false) async function showInfo() { setLoadingState(true) const queryParams = { id: toDoId, } const res = await axios({ method: 'get', url: 'https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item', params: queryParams }) const isSuccess = res.status === 200 if (!isSuccess) alert('smth wrong') if (isSuccess) alert(JSON.stringify(res.data)) setLoadingState(false) } return ( <button onClick={showInfo}> {loadingState ? <Spinner height='16px' /> : <Info />} <style jsx>{` button { all: unset; cursor: pointer; color: grey; display: flex; align-items: center; } button:hover { color: black; } `}</style> </button> ) } function DelBtn({ toDoId, toDosState, setToDosState }) { const [deletingState, setDeletingState] = useState(false) async function remove() { setDeletingState(true) const requestBody = { id: toDoId } const res = await axios({ method: 'delete', url: 'https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item', data: requestBody }) const isSuccess = res.data.Message === "SUCCESS" if (isSuccess) { const updatedToDos = toDosState.filter(item => item.id !== toDoId) setToDosState(updatedToDos) } if (!isSuccess) alert('smth wrong') setDeletingState(false) } return ( <button onClick={remove}> {deletingState ? <Spinner height='16px' /> : <Bin />} <style jsx>{` button { all: unset; cursor: pointer; color: grey; display: flex; align-items: center; } button:hover { color: black; } `}</style> </button> ) } function ToDoInput({ getAllToDos }) { const [addingState, setAddingState] = useState(false) const inputRef = useRef() async function addToList(e) { e.preventDefault() setAddingState(true) const requestBody = { id: shortid(), toDoName: inputRef.current.value, toDoStatus: 'new' } const res = await axios({ method: 'post', url: 'https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item', data: requestBody }) const isSuccess = res.data.Message === "SUCCESS" if (isSuccess) getAllToDos() if (!isSuccess) alert('smth wrong') setAddingState(false) inputRef.current.value = '' inputRef.current.focus() } return ( <form onSubmit={addToList}> <input type="text" ref={inputRef} placeholder='to do...'/> <button>{addingState ? 'Adding...' : 'Add'}</button> <style jsx>{` form { margin: 10px; text-align: center; } form > * { margin: 5px; } input { padding: 0px 3px; max-width: 100%; } button { cursor: pointer; padding: 0px 5px; min-width: 70px; max-width: 100%; } `}</style> </form> ) } // #endregion About CRUD CRUD - C reate, R ead, U pdate, D elete API functionality. Overview Lambda function AWS Lambda is a containerized self-managed server which runs a function. Invoiced for compute time only, not for idle time Scaled & shrunk automatically Dynamo DB It is a no-sql database from AWS API Gateway It is a service from AWS that allows to create APIs points for RESTful and WebSocket APIs IAM IAM - I dentity and A ccess M anagement service Allows to specify who can access which services on AWS and under which conditions DynamoDB Search for DynamoDB service Note down your region from at the top right corner - eu-north-1 Push Create table button Table name - toDoTable , partition key - id Lambda function Search for Lambda service Hit Create function button Function name - toDoApi , runtime - Node.js , execution role - New role from AWS policy templates , role name - toDoRoles IAM In our lambda function under Configuration - Permissions - Execution role click on toDoRoles name We re-directed into IAM to our role Click Add permissions - Attach policies Add CloudWatchFullAccess , AmazonDynamoDBFullAccess API Gateway Search for API Gateway service Create New REST API with name toDoApiGateway In Actions create a Resource with name health , items , item with CORS enabled Select resource and Create method via Actions with adding our lambda function name and region Add different resources with GET , POST , DELETE , PATCH methods connected to our toDoApi lambda function Then Deploy API from Actions dropdown with stage name prod In return we get API end point url to use our API https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod Lambda event and response formats https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html Event Amazon API Gateway invokes your function with an event that contains a JSON representation of the HTTP request. { "resource": "/", "path": "/", "httpMethod": "GET", "requestContext": { "resourcePath": "/", "httpMethod": "GET", "path": "/Prod/", ... }, "headers": { "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "accept-encoding": "gzip, deflate, br", "Host": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36", "X-Amzn-Trace-Id": "Root=1-5e66d96f-7491f09xmpl79d18acf3d050", ... }, "multiValueHeaders": { "accept": [ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ], "accept-encoding": [ "gzip, deflate, br" ], ... }, "queryStringParameters": null, "multiValueQueryStringParameters": null, "pathParameters": null, "stageVariables": null, "body": null, "isBase64Encoded": false } Response { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "isBase64Encoded": false, "multiValueHeaders": { "X-Custom-Header": ["My value", "My other value"], }, "body": "{\n \"TotalCodeSize\": 104330022,\n \"FunctionCount\": 26\n}" } Lambda function code const AWS = require('aws-sdk') const region = 'eu-north-1' AWS.config.update({ region }) const dynamodb = new AWS.DynamoDB.DocumentClient() const TableName = 'toDoTable' function buildResponse(statusCode, body) { return { statusCode, headers: { 'Content-Type': 'application/json', "Access-Control-Allow-Origin": "*", }, body: JSON.stringify(body) // api gateway expects the json string } } async function getItems() { const params = { TableName } const items = await scanDynamoRecords(params, []); const body = { items } return buildResponse(200, body); } async function scanDynamoRecords(scanParams, itemArray) { try { const dynamoData = await dynamodb.scan(scanParams).promise(); itemArray = itemArray.concat(dynamoData.Items); if (dynamoData.LastEvaluatedKey) { scanParams.ExclusiveStartkey = dynamoData.LastEvaluatedKey; return await scanDynamoRecords(scanParams, itemArray); } return itemArray; } catch(err) { return buildResponse(404, {function: 'scanDynamoRecords', err}) } } async function getItem(id) { const params = { TableName, Key: {id} } try { const response = await dynamodb.get(params).promise() return buildResponse(200, response.Item) } catch(err) { return buildResponse(404, {function: 'getItem', err}) } } async function saveItem(requestBody) { const params = { TableName, Item: requestBody } return await dynamodb.put(params).promise().then(() => { const body = { Operation: 'SAVE', Message: 'SUCCESS', Item: requestBody } return buildResponse(200, body); }, (err) => { return buildResponse(404, {function: 'saveItem', err}) }) } async function modifyItem(id, updateKey, updateValue) { const params = { TableName, Key: { 'id': id }, UpdateExpression: `set ${updateKey} = :value`, ExpressionAttributeValues: { ':value': updateValue }, ReturnValues: 'UPDATED_NEW' } return await dynamodb.update(params).promise().then((response) => { const body = { Operation: 'UPDATE', Message: 'SUCCESS', UpdatedAttributes: response } return buildResponse(200, body); }, (err) => { return buildResponse(404, {function: 'modifyItem', err}) }) } async function deleteItem(id) { const params = { TableName, Key: { 'id': id }, ReturnValues: 'ALL_OLD' } return await dynamodb.delete(params).promise().then((response) => { const body = { Operation: 'DELETE', Message: 'SUCCESS', Item: response } return buildResponse(200, body); }, (err) => { return buildResponse(404, {function: 'deleteItem', err}) }) } exports.handler = async function (event) { console.log('Request event: ', event) if (event.httpMethod === 'GET' && event.path === '/health') { const response = buildResponse(200, 'healthy') return response } if (event.httpMethod === 'GET' && event.path === '/items') { const response = await getItems() return response } if (event.httpMethod === 'GET' && event.path === '/item') { const response = await getItem(event.queryStringParameters.id) return response } if (event.httpMethod === 'POST' && event.path === '/item') { const response = await saveItem(JSON.parse(event.body)) return response } if (event.httpMethod === 'PATCH' && event.path === '/item') { const requestBody = JSON.parse(event.body) const response = await modifyItem(requestBody.id, requestBody.updateKey, requestBody.updateValue) return response } if (event.httpMethod === 'DELETE' && event.path === '/item') { const response = await deleteItem(JSON.parse(event.body).id) return response } // rest of all const response = buildResponse(404, '404 Not Found') return response } Check with Postman health GET request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/health item post POST request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item item get GET request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item?id=10003 item patch PATCH request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item item delete DELETE request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/item items get GET request to https://zitdmv25la.execute-api.us-east-1.amazonaws.com/prod/items