Anne's Handmade
August 22nd, 2019
🥡🥡 10 min read

Source Front End

Source Back End

Live Site

Anne's Handmade is a small business owned by a retiree in her 70s. I built this site to help supplement her income and to gain some experience building ecommerce websites. This was a great opportunity to integrate a wide range of features into a single application. My ultimate goal was to create a simple to use website for both the customer and business owner.

landing

Back End

The application is built on top of an GraphQL server powered by Node and backed with a PostgreSQL database. GraphQL is a modern alternative to a traditional REST API and allows the client to query exactly what it needs in one request, from a single endpoint.

Apollo Server

Apollo Server turns a standard Express server into a GraphQL server. I designed a schema using the GraphQL Query Language which acts like a blueprint for all the operations available. All the operations are then implemented in resolvers.

  • Simplified example of a schema
type Mutation {
  signin(email: String!, password: String): User
}

type User {
  id: ID!
  name: String!
  email: String!
  password: String!
}
  • Example of the signin resolver
const bcrypt = require('bcryptjs')
const validateInput = require('../utils/validateInput')
const createCookie = require('../utils/createCookie')
const signToken = require('../utils/signToken')

module.exports = async (_, args, ctx, info) => {
  // validate email, password
  validateInput('signin', args)

  // check if there is a user with that email
  const user = await ctx.prisma.user({ email: args.email.toLowerCase() })

  // if no user throw error
  if (!user) throw new Error(`no user for email ${args.email}`)

  // check if password is matches database
  const valid = await bcrypt.compare(args.password, user.password)

  // if no match throw error
  if (!valid) throw new Error('invalid password')

  // create token
  const token = signToken(user.id)

  // create cookie
  createCookie(ctx.res, token)

  // return user
  return user
}

Prisma

Prisma acts as an ORM and allows the application to communicate with the database. Prisma is nice to work with because all the developer has to do is supply a datamodel it creates the entire data layer API. Prisma comes with a handy CLI tool that makes interacting with the database easy from the command line.


Playground

A benefit of GraphQL is the Playground feature. When the backend server is running, a GUI is available at /graphql where Queries and Mutations can be ran against the database. This is great for making sure everything works the way it is intended while in development.

  • A simple Query displaying a list of all products in the database.
playground

Email

The server has the ability to send email to users when they interact with the site. The most important event is when a user purchases a product. When this occurs the user is sent a Thank You note along with the invoice for their purchase. The email contains some branding, a summary of products purchased and links back to the site.

  • Thank You email
reciept

Permissions

The API allows for powerful operations such as creating, updating and deleting products. I built a permission system that protects the application from these operations being performed by bad actors. The ADMIN role comes with elevated privledges that allow the user to execute these powerful actions. The default role is USER, and people that signup are automatically assigned this role. In fact, the only people that have the ADMIN privledges are myself and the business owner.

This system works by checking the user's credentials which are sent inside a cookie with all requests. The cookie contains the user's ID, which is used to look them up in the database. The role property is then examined to see if it matches the requirements for the operation requested. If the requirement is not met an error is thrown and the operation is denied.

  • Permissions can be tested in the Playground. This example illustrates attempting to delete a product without the role of ADMIN.
permissions

AWS S3

AWS S3 is used to store product images. Since the ADMIN has the ability to create and delete products from the front end user interface I added the ability to upload and delete images from AWS S3. This is a nice feature that makes adding new products much more efficient. Without the integration the images would have to be added to S3 separately which would be a pain.


Google Sheets

I used Google Sheets, a spreadsheet, as the single source of truth for all of the product data. I was able to integrate the Google Sheets API with my backend via a database seeding script. This was useful in development because I could drop the database and easily re-seed all the products when I made changes.


Front End

The user interface is built with Nextjs, a React framework for building server-side rendered applications.

Images

As a side note, I took all the photos for this site myself. I used the trunk of my white Honda Accord as the backdrop. I went through a tedious process to prepare the images for use on the web. Each image had to be cleaned up and cropped to a square using Gimp. Then I used JPEG Optimizer to compress the images. Keeping the images small keeps overhead low by reducing the storage space needed and by shrinking the amount of data transferred. This is also good for performance. I was able to reduce the size of each image from ~400KB to ~30KB.

Shopping Cart

The Cart is a standard ecommerce feature where a user can place items they intend to purchase. A product can be added to the Cart by clicking the 🛒 icon from the search results page or by clicking the Add to Cart button from the product page. The Cart itself can be toggled in and out of view by clicking Cart on the page header. The number of items currently in the cart is always displayed.

  • Catalog Page cart 2

  • Product Page cart 3

  • When the Cart is opened each item is listed as well as a price breakdown including item price, tax, subtotal and total.

cart 1

Stripe Checkout

Stripe Checkout provides a simple form that can recieve all the input necessary to complete a sale. When the user submits the form, the data is tokenized and sent to back end which then reaches out to Stripe to approve the sale. Once approved the back end creates a record of the sale including the shipping address of the user and the id of the sale on Stripe.

  • Stripe Checkout makes the purchase flow smooth and easy for the user.

Responsive Design

In today's landscape a website needs to look good on a wide variety of screen sizes from televisions to smart phones. This is accomplished through the use of Media Queries. An example of this can be seen in the Catalog component. On a large screen products are displayed in a four column layout, but when the screen is small the layout is changed to one column. Notice the header is also different on smaller screens.

  • Catalog on 1360px wide screen.
catalog 2
  • Catalog on 375px wide screen.
catalog 3

Apollo Client

Apollo Client acts as the bridge between the user interface and the back end GraphQL server and enables several key features. The products available at Anne's Handmade are classified into three categories - NECKLACE, BRACELET or EARRINGS, as well as several bead types - RED_JASPER, BLACK_ONYX, etc. When the user is browsing the catalog these parameters are fed to Apollo Client via query parameters in the page URL. Once Apollo Client has these parameters is queries the database and returns the matching products.

  • Catalog displaying products with a bead type of RED_JASPER.
catalog 1

Pagination

Pagination is another feature made possible by Apollo Client. This means only a fixed number of results are returned with each request. This prevents requests from becoming too big and lets the user focus on a small number of products at a time. The user can browse pages by clicking Next and Previous.


The Search feature uses Downshift to give the user a preview of any item matching their search criteria. A drop down is displayed with the name of the product and a small thumbnail image. When an individual item is clicked the user is brought to that product's page.

  • Searching for the term buddha fetches 6 matches.
search

Orders

  • A user can easily pull up their entire purchasing history.
orders

Admin Dashboard

Only users with the ADMIN role can access the powerful Admin Dashboard. From this interface the ADMIN can create, update and delete products, as well as view sales records and inventory. The interface is built with React Table.

  • React Table in action.
admin 3
  • Viewing a sales record to get the user's shipping address and toggle the shipped status to true. A link is also available to pull up the sales record on Stripe.
admin 1
  • Viewing an inventory record to update a product property, add/remove an image and raise/lower the price.
admin 2

CI Build System

Travis is used to make sure all tests are passing before new code is sent to production. When new commits are pushed to version control Travis is triggered and the application is built on their servers. If the build succeeds a suite of tests I wrote is run with Jest. Only when all of these tests pass are the commits pushed to version control and the new code deployed.

Deployment

Currently, Anne's Handmade is deployed on the free tier of Heroku. This is not ideal in terms of performance. Using this setup there are three separate servers - one each for the front end, back end and database, all of which go into sleep mode when not being used. This means that there is considerable loading time when the site is visitted for the first time after a period on inactivity. In the future I hope the business owner will let me devote more resources to deployment.