Setting up an online store with Angular, Snipcart and Firebase
Setting up an online store with Angular, Snipcart and Firebase

How to Set Up an Online Store with Snipcart, Angular & Firebase?

A guide for developers to setup an online store using an Angular website, Firebase Realtime Database and Snipcart eCommerce platform.

Alan Acuña
7 min readApr 8, 2020

--

Do you want to build an online store? There are many platforms out there that can help you set up an online store. But maybe you are a developer, maybe you build websites with a JavaScript framework like Angular, isn't there a way to set up an online store with what you already know and use? Well, lucky for you there is. In this post we are going to set up an online store using Angular, Firebase and the eCommerce platform, Snipcart.

Requirements

To follow this example you must be familiar with the Angular framework and you need the Angular CLI tool installed in your computer. It is also recommended to know about Firebase, although we will only use it to store the information of our products.

Starting the Project

First, we are going to create the Angular application. Open the terminal in the folder of your choosing (like your desktop, for example) and execute the command ng new snipcart-angular. Once the application is created we can execute the command ng serve-o to keep track of the changes we make to it.

Optionally, you can make use of the example project to follow this post. You can find it in this GitHub repo.

Now we are going to create a Firebase project. Access the Firebase console, click on “add project” and follow the steps.

Firebase Console
Adding a project in the Firebase Console

Once our project is created, select Database, under Development in the side menu. We are not going to use the Cloud Firestore option, instead go down to the Realtime Database (RTDB) part and click on “Create database”. Why not choose Cloud Firestore for this project? Because the Realtime Database saves information in JSON format and has a REST API to make queries, which we will deal with later for the Snipcart integration.

Choosing Realtime Database
Choosing the Realtime Database in the Firebase Console

We do not need to install Firebase dependencies in our Angular application, since we are going to use the RTDB REST API.

Setting Up Snipcart

Snipcart is an online eCommerce platform that’s extremely easy to integrate with any website. This platform includes features such as a dashboard to manage all your orders, products, inventory, etc. It also allows you to manage your payments with PayPal, Stripe, among others. To know more about Snipcart visit their website.

To start working with Snipcart the first thing to do is create an account. Once this is done, you will enter the Snipcart dashboard.

To integrate our Angular application with Snipcart we first need our Snipcart API key. Keep in mind that the test environment API key is different from the production API key, so you have to change it when your online store is ready. The API key is found in the right side menu → Accounts → API keys. On this page we will find a snippet of code like this:

<link rel=”stylesheet” href=”https://cdn.snipcart.com/themes/v3.0.9/default/snipcart.css" /><div id=”snipcart” data-api-key=”YOUR-API-KEY” hidden></div>
<script src=”https://cdn.snipcart.com/themes/v3.0.9/default/snipcart.js"></script>

We copy this snippet of code and paste it into the index.html of our Angular application. Paste the CSS in the head and the rest at the end of the body. With this done we are ready to work with Snipcart in our application.

Defining Our Products

Now we have to define our products so that they can be used by Snipcart. To achieve this we need to add different attributes to the HTML element that will be our purchase button. The Snipcart docs has this example.

<button class="snipcart-add-item"
data-item-id="starry-night"
data-item-price="79.99"
data-item-url="/paintings/starry-night"
data-item-description="High-quality replica of ..."
data-item-image="/assets/images/starry-night.jpg"
data-item-name="The Starry Night">
Add to cart
</button>

There are other attributes defined by Snipcart but the ID, name, price, URL, description and image are required, so we need our products to contain these fields.

What we are going to do is store the information of our products in the RTDB and, making use of its REST API, bring that information to our Angular application. So first we have to add our products to the RTDB either manually or by importing a JSON file (recommended). It is very important that our products have a structure like the following so that they correspond to Snipcart’s definition of products.

[{
"category": "Category",
"description": "Product 1 description",
"id": "P001",
"image": "image-url/product1.jpg",
"name": "Product 1",
"price": 1000,
"url": "https://YOUR-FIREBASE-PROJECT.firebaseio.com/products.json"
},
...
]

If you wonder where the URL https://YOUR-FIREBASE-PROJECT.firebaseio.com/products.json came from, it is how the REST API of the RTDB is used. According to the Firebase docs:

We can use any Firebase Realtime Database URL as an endpoint of REST. All we have to do is append .json to the end of the URL…

This URL will be the same for all defined products. Also, make sure that anyone has read access to your products using the RTDB rules.

Setting Up Our Products in the Angular Application

Now that we have our products stored in the RTDB it is time to use them in our Angular application. To start, we open the app.module.ts of our application and import the HttpClientModule into the imports array, like this.

// Other imports
...
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [...],
imports: [
...,
HttpClientModule
],
...
})
export class AppModule { }

Now we open the app.component.ts. We can first define a Typescript interface outside the class to work more easily with our products as shown below, but this step is optional.

// Products interface
interface Product {
id: string;
category: string;
name: string;
price: number;
url: string;
description: string;
image: string;
}

Staying in the app.component.ts and inside the class we define the property products$: Observable<Product[]> and inject the HttpClient into the constructor, making sure to import what is necessary. Now we need to define a method that gets the products from the RTDB, like so:

// Method to obtain our products from the RTDB
getProducts() {
return this.http.get<Product[]>('https://YOUR-FIREBASE-PROJECT.firebaseio.com/products.json');
}

And inside the ngOnInit method, we are going to call getProducts() to initialize our property products$, this way:

ngOnInit() {
this.products$ = this.getProducts();
}

Next, we open the app.component.html to display our products. We must remember the example of the Snipcart shopping button, however, since we are working with Angular we are going to do it dynamically. You can choose the presentation you want to show your products, but the important thing to work with Snipcart is that shopping button.

<div *ngFor="let product of products$ | async">
...
<button class="snipcart-add-item"
[attr.data-item-id]="product.id"
[attr.data-item-price]="product.price"
[attr.data-item-url]="product.url"
[attr.data-item-description]="product.description"
[attr.data-item-image]="product.image"
[attr.data-item-name]="product.name"
[attr.data-item-categories]="product.category">
Buy now
</button>
</div>

As an explanation to the previous snippet, we use the pipe async to unwrap our property products$ which is an observable. At the same time we use the *ng-for directive to create one item for each product. For the purchase button we link the attributes required by Snipcart with the properties of our products.

If you’ve worked with Angular before, you might be wondering why not just use [data-item-x]="product.x"? This will throw an error, since Angular doesn’t recognize data-item-x as a valid attribute of the <button> element, so it needs to be done the way the snippet is.

Lastly, to access the shopping cart and make a purchase, Snipcart needs an element like the next one:

<button class="snipcart-checkout">Click here to checkout</button>

You can add this button in the header of your Angular application.

Validating Purchase Orders

Every time a purchase order is made with Snipcart, the platform performs a validation of said order. This validation takes into account the URL attribute of the product and makes an HTTP request to this URL. This URL can be the domain of your website, Snipcart will verify that the information in the purchase order corresponds to the information on your website. However, since we are working with an Angular application, this validation will always fail, since our application is rendered on the client side.

There are several options to validate the purchase orders of our Angular application, such as using Angular Universal to render our application on the server side. But we are going to do it in a simpler way. The product URL can also be the address of a JSON file. In this case, Snipcart will verify that the purchase order information matches the information in the JSON file. This is the main reason why we have been working with RTDB, once again we can take advantage of its REST API to get this validation.

We are going to enter our Snipcart Dashboard and select on the right side menu → Domains & URLs. Here we are going to add our main domain in the section marked “DEFAULT WEBSITE DOMAIN”, and in the section “OTHER DOMAINS & SUBDOMAINS” we are going to add the URL of our products (YOUR-FIREBASE-PROJECT.firebaseio.com/products.json). Be sure to choose the HTTPS option to add the latter.

Snipcart Domains & URLs
Manage your domains in the Snipcart dashboard

At this point you can test your online store and make sure everything works. With this we already have our online store ready with Snipcart, Angular and Firebase. Remember that we have an example project available in this repository, in case you need extra help.

--

--

Alan Acuña

Hi there! I have a degree in biomedical engineering but I’m a self taught full-stack developer. I specialize in Angular & Ionic development.