DEV Community

Cover image for Serverless Shopping Cart with OpenJS Architect
Paul Chin Jr.
Paul Chin Jr.

Posted on

Serverless Shopping Cart with OpenJS Architect

In this post, we'll be looking at creating a model of a shopping cart. We will be using OpenJS Architect for the infrastructure as code and deployment framework. We will be deploying to AWS cloud through Begin. No other frameworks will be needed as we'll be developing this app with just HTML forms, GET, and POST requests. The cart will even work with javascript disabled!

Create a new Architect project

We can create a new Architect project from the command line, the only prerequisite is having Node.js installed.

npm init @architect ./shopping
cd shopping
npm i @architect/functions @begin/data
Enter fullscreen mode Exit fullscreen mode

We're adding two dependencies that we will need to make things easier, @architect/functions and @begin/data. These two libraries will make things easier as we work with AWS Lambda functions and DynamoDB.

Modify the app.arc file

The app.arc file is our infrastructure as code file. It tells Architect what resources to deploy and gives structure to the underlying source code. Open up the app.arc file in the root of your project and modify it with the following:

@app
shopping

@http
get /
post /cart
post /cart/delete

@tables
data
  scopeID *String
  dataID **String
  ttl TTL
Enter fullscreen mode Exit fullscreen mode

After you modify the app.arc file, run arc init from the command line to scaffold out the functions that we will need.

There are three routes and a declaration of a data table. Each route will have a corresponding Lambda handler that is a discrete function. We'll be using @begin/data as a DynamoDB client to interact with the database.

Modify the get-index route

The first route we'll look at is the get-index route, which is called at the apex of the application.

let arc = require('@architect/functions')
let data = require('@begin/data')

async function http (req) {

  let result = await data.get({
    table: 'shopping-cart'
  })

  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: `
    <h1> Praise Cage </h1>
    <p> Welcome to the Store </p>
    <ul>
    <li>Item 1</li>
    <form action='cart' method='POST'>
    <input type="number" name="quantity"/>
    <input type="hidden" name="productId" value='001'>
    <button>Add to Cart</button>
    </form>
    <form action='cart/delete' method='POST'>
    <input type="hidden" name="productId" value='001'>
    <button>Remove from Cart</button>
    </form>
    <li>Item 2</li>
    <form action='cart' method='POST'>
    <input type="number" name="quantity"/>
    <input type="hidden" name="productId" value='002'>
    <button>Add to Cart</button>
    </form>
    <form action='cart/delete' method='POST'>
    <input type="hidden" name="productId" value='002'>
    <button>Remove from Cart</button>
    </form>
    <li>Item 3</li>
    <form action='cart' method='POST'>
    <input type="number" name="quantity"/>
    <input type="hidden" name="productId" value='003'>
    <button>Add to Cart</button>
    </form>
    <form action='cart/delete' method='POST'>
    <input type="hidden" name="productId" value='003'>
    <button>Remove from Cart</button>
    </form>
    </ul>
    <p>This is your order</p>
    <ul>${result.map(item => `<li>${JSON.stringify(item.cartItem)}</li>`).join('')}</ul>
    `
  }
}

exports.handler = arc.http.async(http)
Enter fullscreen mode Exit fullscreen mode

In this function, we are returning a string of HTML that will be rendered by the browser. Congratulations, you now have done server-side rendering with a serverless function!

Adding a database layer

You should notice that we are using @begin/data in the get-index route to grab all the cart data before the page loads and we will render that at the bottom of the page. In a future tutorial, we will look at styling this page, but for now, we will just return JSON of the objects in the database.

Modify the post-cart route

The post-cart route is where we will update our cart with the different items that are available.

let arc = require('@architect/functions')
let data = require('@begin/data')

async function http (req) {

  await data.set({
    table: "shopping-cart",
    key: req.body.productId,
    cartItem: {
      quantity: req.body.quantity,
      prodId: req.body.productId
    }
  })

  return {
    statusCode: 302,
    location: '/'
  }
}

exports.handler = arc.http.async(http)
Enter fullscreen mode Exit fullscreen mode

Modify the post-cart-delete route

The post-cart-delete handler is responsible for deleting the item from the cart. Again it uses the @begin/data DynamoDB client. For more information about @begin/data, check out the docs.

let arc = require('@architect/functions')
let data = require('@begin/data')

async function http(req) {

  await data.destroy({
    table: "shopping-cart",
    key: req.body.productId
  })

  return {
    statusCode: 302,
    location: '/'
  }
}

exports.handler = arc.http.async(http)
Enter fullscreen mode Exit fullscreen mode

With this last handler, we can now run npm start and see the results of our app thus far.

You should be seeing an unstyled page.

Alt Text

Next steps

In a follow-up tutorial, we will take a look at styling the cart so it will look a bit nicer.

Alt Text

Full code repo can be found at https://github.com/pchinjr/shopping-cart

Top comments (0)