Building CartoonStock.com
(in short)

CartoonStock is an online library with over 500,000 licensable cartoons as well as having the option to buy merchandise with cartoons on.
The site is used by around 300,000 users per month servicing customers across the globe from individuals to large scale publications.

The Task

The task given to myself and one other developer was to rebuild the entire stack for the website. We were given large freedom on what technologies we used, I was given the task of the frontend as well as contributing towards the database, backend and API. The site was to be built as a MPA (multi-page application) with some pages being highly interactive with SPA elements within. I came into this task as a junior developer (no commercial experience).

The Stack

Database rebuild/migration

While the designer worked on the designs for the new site I worked with the other developer to redesign the database and construct the API to interact with it.


Our database contains many tables, some with tens of millions of rows. We did the migration buy building migration scripts for each table that needed them in PHP. We could then run these when needed and it would translate all the data across from the old format into the new.
We also had post-migration scripts that could only be run once the main migration was complete. An example of this is the way we split out our orders in the new system - We have an orders table which then uses foreign keys to reference a table which contains each orders items.
This was one of the big changes we made, to seperate things out and use foreign keys to cross reference data, this speeds up querying the database as it's easier to target just the specific data you want rather than scanning through a table with many many columns and data unecesarry to the initial query, it also makes it easier to manage data by using a forgeign key to fetch/modify relevant data from multiple tales.

On top of this our new database is serverless with auto-scaling using AWS Aurora RDS Serverless MySQL.

API

For the API we decided to build it in a serverless fashion with AWS Lambda functions written in NodeJS and connect them to AWS API Gateway.

We also created documention for this REST API, to do this we used Slate.


api documentation

Vanilla JS, like a framework

Although the choice was made not to use a framework, I still like the principals that framworks use, which are possible in vanilla JS, here's how I did this:


I love the use of components where a component can contain its own logic and styling, to do this with vanilla JS I used Web Components.


Here's an example of how I structure a component in TypeScript:

web component image

To add data to the component, variables can be added above the constructor on line 3 and used throughout the component as a global variable.


The style tags contain all the CSS which is encapsulated to this component only. The connectedCallback function is called when the component is attached to the page, this is a good place to fetch data and attach event listeners.

Below the connectedCallback other functions can be created for more logic as well as getter and setter functions to interact with other components.


But how about communicating with other components across the DOM? - This is where the Event Bus comes in...


custom event bus image

This can now be called anywhere on the site. To dispatch an event:

event bus dispatch image

and to listen for the event:

event bus listener image

For data sharing between pages localStorage is used, otherwise data is stored in the database.

The Process

The decision was made not to use frameworks or plugins so the code on the site would remain functional over the years. The only microsevice used was axios, to make API calls a little nicer, everything else on the site was coded from scratch by myself, including the dynamic image grid which can be seen across the site such as the search results page


To build the image grid I took inspiration from this ShutterStock developer blog article.


Here's a snippet of how I translated this code to work with my Vanilla JS/TS.


Each image from the API contains the image dimensions to assist with the algorithm.


The function was created in a way that you can pass in a target row height (in pixels) as a parameter and the algorithm will take care of the rest.

grid example one

Then all that's left is to tidy up if there's an empty row at the end and apply the correct height to each row based on the algorithm.

grid example two

The result looks like this:

cartoonstock image grid

Another good example of something complex and reactive on the site is this purchase options panel...

purchase options panel

Firstly certain images have blanket restrictions applied to them, so the panel will only generate the options that are available for that image.
Then the user can step forwards and backwards through the options while also jumping back to certain steps. To acheive this I assigned an ID to each option in a way for example if you chose the first option you get '1', then if you chose option 3 on the next menu the ID would be come '1-2' (with 0 being the first option) and so on. - this is not the exact ID system used but this is the concept.
For the Gift Options the menu options have pricing that updates based on the users selection - we took inspiration from the way Apple does this when configuring a purchase on their site.
Selecting a product also opens a gallery in place of the cartoon when in Gift Options.
Another complexity is some cartoons are restricted based on license or merchandise type, so when the user wants to add an item to their cart we have to call the API and check for any restrictions.
If there are restrictions we then need to trigger a prompt for the user to ask some extra information such as usage and country or usage.

Soo much more

There's soo many more things I can talk about, the site is very complex but I think this sample is enough for now.

Some other key site features include the ability to create favourites collections, rename, share, manage and delete them.
A license pack system where users can buy licenses in bundles in advance and redeem them accross the site at any time, an adress book and saved (tokenised) cards.

favourites collection We also have a nice security setup, but I'm not going to talk about that here 😉