All

Ecommerce without a server-side language

What’s the best server side language for building an eCommerce platform? Python? NodeJs? Ruby? PHP?
The answer is none of them, and any of them. They all do the same thing. Some are just a little more elegant and mature than others. It’s all so boring!

Thats why I’ve decided to experiment with an idea I have had for a number of years.
I want to try and build a simple online store using no server-side language at all. Using XML directly output from an XML database and transforming it with XSLT.

Tools

  • BaseX: a lightweight, high-performance and scalable XML Database.
  • Nginx: used as a proxy to the BaseX REST API.
  • XSLT: transforms our XML to HTML.
  • LocalStorage: allows you to use JavaScript to store data locally in a user’s browser.
  • jQuery: Just as a helper

Install Basex

wget http://files.basex.org/releases/8.6.1/BaseX861.zip
unzip BaseX861.zip
mv basex /

Basex requires the Java runtime environment

sudo apt-get install default-jre

Fire up the rest client

./basex/basexhttp

View the result, basexhttp runs on port 8984 so go to http://localhost:8984.
It also has basic auth, so when prompted answer…
username: admin
password: admin

Install nginx

sudo apt-get install nginx

Setup our proxy

vim /etc/nginx/sites-available/default

and copy and paste the following:

server {
        listen 80;
        root /var/www/html ;

        location = / {
            proxy_pass http://127.0.0.1:8984/rest/shop/data;
            proxy_set_header Authorization "Basic YWRtaW46YWRtaW4=";
            xslt_types application/xml;
            xslt_stylesheet /var/www/html/shop.xslt;
        }

        location ~ ^/(basket|checkout) {
            try_files $uri.html = 404;
        }
}

“YWRtaW46YWRtaW4=” is base64(admin:admin) which is the default basic auth for basex
*Note that this header is open to the world

service nginx restart

Create our XML Database

You can add database(s) and xml via the restful inteface. We use postman, but you could just as easily use curl, ajax etc.

Create the database “shop”:

PUT http://127.0.0.1:8984/rest/shop

Add some data

PUT http://127.0.0.1:8984/rest/shop/data Content-Type: application/xml

Posting the following data:

<categories>
  <category>
  	<name>Category One</name>
  	<products>
      <product>
        <name>Product One</name>
        <price>99</price>
      </product>
    </products>
  </category>
  <category>
  	<name>Category Two</name>
  	<products>
      <product>
        <name>Product Two</name>
        <price>49</price>
      </product>
    </products>
  </category>
</categories>

Nginx applies the following XSLT to the XML output

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8"
        indent="yes"
        method="html"
        media-type="text/html"
        omit-xml-declaration="yes"
        doctype-system="about:legacy-compat"/>
<xsl:template match="/">
<head>
    <meta charset="utf-8"/>
    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
    <script type="text/javascript" src="shop.js"> </script>
</head>
<body>
  <h1>Welcome to our example shop</h1>
  <xsl:for-each select="categories/category">
    <h2><xsl:value-of select="name"/></h2>
    <xsl:for-each select="products/product">
        <h3><xsl:value-of select="name"/></h3>
        <p>Price: £<xsl:value-of select="price"/></p>
        <button>Add to cart</button>
    </xsl:for-each>
  </xsl:for-each>
</body>
</xsl:template>
</xsl:stylesheet>

Visting our localhost, we now see a list of our products

LocalStorage

The shop.js we inserted into our XSLT (as well as Jquery) performs a single action. Add to basket.

$(function(){
  $('button').click(function(){
    var name = $(this).prevAll('h3').html();
    var price = $(this).prevAll('p').html();
    price = price.replace(/[^0-9\.]/g, '');
    var item = {};
    item.name = name;
    item.price = price;
    localStorage.setItem('basket', JSON.stringify(item));
    window.location.replace('/basket');
  });
});

The Checkout

Adding a product redirects the user to their basket and integrates with a basic Paypal form to allow a checkout.

Our basket page:

<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
    <input type="hidden" name="cmd" value="_cart">
    <input type="hidden" name="business" value="[email protected]">
    <input id="name" type="hidden" name="item_name" value="">
    <input type="hidden" name="item_number" value="123">
    <input id="amount" type="hidden" name="amount" value="">
    <p>You are buying <span class="name"></span> @ &pound;<span class="amount"></span></p>
    <input type="image" name="submit"
      src="https://www.paypalobjects.com/en_US/i/btn/btn_buynow_LG.gif"
      alt="PayPal - The safer, easier way to pay online">
  </form>

<script>
$(function(){
  var basket = JSON.parse(localStorage.getItem('basket'));
  $('#name').val(basket.name)
  $('.name').html(basket.name);
  $('#amount').val(basket.price);
  $('.amount').html(basket.price);
});
</script>

Final thoughts

It wasn’t quite as straight forward to setup as I had hoped. I love the idea that we are getting the XML output directly from the API running on the server proxied to port 80. The TTFB was lower than any site I’ve developed with a server side language.

I’m no expert when it comes to BaseX or XML/XSLT and I enjoyed the ride, but I think I’ll be sticking to using server side languages.

Insights by Trevor Sewell

Share this post