Today we're going to create a small voting app for my favorite Nicolas Cage movies. We will have multiple endpoints that are deployed directly to AWS with Begin. We will also be using OpenJS Architect as the deployment framework and IAC tool.
Clone repo and local development
The first step is to click the button to deploy this app to live infrastructure with Begin.
Underneath, Begin will create a new GitHub repo to your account to clone to work on locally. Each push to your default branch will trigger a new build and deploy to the staging
environment. Your CI/CD is already complete!!
When your app deploys, clone the repo, and install the dependencies.
git clone https://github.com/username/begin-app-project-name.git
cd begin-app-project-name
npm install
First, let's look at the app.arc
file. It gives us an overview of the entire app.
# app.arc
@app
begin-app
@http
get /
post /movies/:movieID
post /downvote
post /upvote
@tables
data
scopeID *String
dataID **String
ttl TTL
In this .arc
file, we have four routes. Each route is a Lambda function that is triggered by an HTTP method.
Let's take a look at the POST function, post-movies-000movieID
, to add a movie to the database.
// src/http/post-movies-000movieID/index.js
let data = require('@begin/data')
let arc = require('@architect/functions')
exports.handler = arc.http.async(route)
async function route(req) {
await data.set({
table: 'movies',
key: req.pathParameters.movieID,
votes: 0,
title: req.body.title
})
return {
statusCode: 301,
location: '/'
}
}
Now we can use an HTTP client like Insomnia or a CURL request to POST a movie with a movieID
and movie title
. Adding movies to the database first will let us server render the UI needed for the vote buttons.
curl --request POST \
--url http://localhost:3333/movies/001 \
--header 'Content-Type: application/json' \
--data '{
"title":"National Treasure"
}'
Replace the path param with a unique movieID and a data payload of "title". You can keep adding movies, but I just added a couple to get the functionality working.
Once we have some objects in the database, let's create the front end. Take a look at the get-index
route.
// src/http/get-index/index.js
let arc = require('@architect/functions')
let data = require('@begin/data')
exports.handler = async function http(req) {
let result = await data.get({ table: "movies" })
let element = result.map(movie).join('')
function movie(obj) {
return `
${obj.title} : ${obj.key} votes: ${obj.votes}
<form action="/upvote" method=post>
<input type=hidden name=movieId value=${obj.key}>
<button>Upvote</button>
</form>
<form action="/downvote" method=post>
<input type=hidden name=movieId value=${obj.key}>
<button>Downvote</button>
</form>
`
}
let html = `<h1> Praise Cage </h1>
<div>${element}</div>
`
return {
statusCode: 200,
headers: {
'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0',
'content-type': 'text/html; charset=utf8'
},
body: html
}
}
Now we can see the movies that are added, when a user requests the get-index
page the function will do a lookup for all the movies in the database and generate two form buttons for downvote
and upvote
as well as render the current votes and title. You should see a simple UI rendered to the index page.
Next, we can write a function for upvoting a movie.
// src/http/post-upvote/index.js
let arc = require('@architect/functions')
let data = require('@begin/data')
exports.handler = arc.http.async(route)
async function route(req) {
let movieId = req.body.movieId
let table = "movies"
let incr = await data.incr({ table, key: movieId, prop: "votes" })
console.log("votes incremented to", incr)
return {
statusCode: 301,
location: '/'
}
}
Finally, we can create a function for downvoting a movie.
let arc = require('@architect/functions')
let data = require('@begin/data')
exports.handler = arc.http.async(route)
async function route(req) {
let movieId = req.body.movieId
let table = "movies"
let decr = await data.decr({ table, key: movieId, prop: "votes" })
console.log("votes decremented to", decr)
return {
statusCode: 301,
location: '/'
}
}
The voting functions use Begin Data client for DynamoDB. Begin data has an API for atomic counters using .incr
and .decr
so we know that each of these click operations is going to result in a count down and count up.
Conclusion
We have a full app with server-side rendered markup hydrated from a database!
See the completed app here and vote for your favorite Nicolas Cage movie. Full source code is here: https://github.com/pchinjr/begin-new-cage-movies
Top comments (1)
Pikashow combines convenience, affordability, variety, and quality to provide a compelling entertainment experience for its users, making it a preferred choice for many seeking a comprehensive streaming platform by Apk file.