Day 1 - Defining the backend and API

Published by Michael Banzon on Mon Feb 13, 2017

This is the first part of the going full stack series. Read the introduction if you haven’t yet done so.

In this post we will start off by defining the backend and API of the currency conversion program/service we are building to give us a solid foundation to start working on implementation.

The general idea is to provide an API for converting amounts between currencies in bulk. Furthermore we provide a system for setting up webhooks for notification of currency changes. For the website and mobile apps we need a way to get all the currency rates (the frontend code will do the conversion and calculation) plus a way to fetch historical rates for any currency.

Getting currencies?

The first problem that comes to mind is where to get currency rates that are up to date. There are several problems surrounding currencies (the difference between buy and sell rates and the valuation between different currencies) but we are not going to go into details about these here - we simply just need a reliable source of currency rates.

The European Central Bank (ECB) has an XML file on their server with their reference rates. This XML can be fetched via HTTP and that pretty much solves our problem of getting the rates. The ECB doesn’t have all the currencies in the world - but for the sake of our project we’re going to assume that the ones provided will be sufficient.

The XML file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
  <gesmes:subject>Reference rates</gesmes:subject>
  <gesmes:Sender>
    <gesmes:name>European Central Bank</gesmes:name>
  </gesmes:Sender>
  <Cube>
    <Cube time='2016-04-01'>
      <Cube currency='USD' rate='1.1432'/>
      <Cube currency='JPY' rate='128.07'/>
      <Cube currency='BGN' rate='1.9558'/>
      <Cube currency='CZK' rate='27.030'/>
      <Cube currency='DKK' rate='7.4503'/>
      <Cube currency='GBP' rate='0.79890'/>
      <Cube currency='HUF' rate='313.30'/>
      <Cube currency='PLN' rate='4.2440'/>
      <Cube currency='RON' rate='4.4693'/>
      <Cube currency='SEK' rate='9.2413'/>
      <Cube currency='CHF' rate='1.0946'/>
      <Cube currency='NOK' rate='9.4401'/>
      <Cube currency='HRK' rate='7.5105'/>
      <Cube currency='RUB' rate='77.5430'/>
      <Cube currency='TRY' rate='3.2285'/>
      <Cube currency='AUD' rate='1.4884'/>
      <Cube currency='BRL' rate='4.1296'/>
      <Cube currency='CAD' rate='1.4894'/>
      <Cube currency='CNY' rate='7.3903'/>
      <Cube currency='HKD' rate='8.8652'/>
      <Cube currency='IDR' rate='15007.54'/>
      <Cube currency='ILS' rate='4.3182'/>
      <Cube currency='INR' rate='75.8528'/>
      <Cube currency='KRW' rate='1315.78'/>
      <Cube currency='MXN' rate='19.8996'/>
      <Cube currency='MYR' rate='4.4382'/>
      <Cube currency='NZD' rate='1.6518'/>
      <Cube currency='PHP' rate='52.615'/>
      <Cube currency='SGD' rate='1.5389'/>
      <Cube currency='THB' rate='40.115'/>
      <Cube currency='ZAR' rate='16.8758'/>
    </Cube>
  </Cube>
</gesmes:Envelope>

This file is pretty simple and it even contains the date of the rates it holds. The only thing needed is the (long, normal speech) names of the currencies it holds. We’ll fix that when we start building.

It is pretty obvious that we need to fetch the currencies via HTTP - and that needs to happen at least once per day (24 hours) but we need to take the possible timezone difference and variable update times into account which propably means that we should do it more - maybe once per hour just to be safe.

When fetched the currency data needs to be parsed (strictly speaking we could do without this but it will most likely end up shooting us in the foot - or even both feet - later on) and when parsed put in to some kind of persistent storage (definition is intentionally vague here).

This is all a job for a process running in the background - it is not a part of the backend that anyone should interact with directly - it should just be running when needed.

The API

In the description of the backend/system we mentioned what the backend and API should be able to do - from that we can define an API with endpoints that looks like the following.

All endpoints will receive and return data that is JSON formatted.

/currencies GET, POST

This endpoint will return a JSON representaion of all the currencies. The GET version is the currencies relative to the standard base currency (EUR) and the POST version will accept a parameter containing the name of the currency to be used as base currency and the rates returned should be calculated accordingly.

The content of the POST request, to make the base currency british pounds/pound sterling will look like this:

{
  "base_currency": "GBP"
}

The currency response from both the POST and GET will look like this:

{
  "currency_date": "2016-04-01",
  "base_currency": "GBP",
  "rates": [
    {
      "name": "USD",
      "rate": 1,43097
    },
    {
      "name": "DKK",
      "rate": 9,32570
    },
    ...
  ]
}

/convert POST

This endpoint will convert some amounts (maybe only one) from one currency to another. The payload will look like this:

{
  "target_currency": "USD",
  "base_currency": "GBP",
  "amounts": [
    14,
    9,
    4.3125,
    5.5,
    ...
  ]
}

The response will keep the ordering of the amounts the same and will return responses that look like this:

{
  "base_currency": "GBP",
  "target_currency": "USD",
  "currency_date": "2016-04-01",
  "converted_amounts": [
    9.783590,
    6.289451,
    3.013695,
    3.843553,
    ...
  ]
}

/webhook POST, DELETE

This is the endpoint for adding and removing webhooks. When calling this it is expected that the URL given would be able to receive a POST request with a JSON payload corrosponding to the one returned by the /currencies endpoint.

The POST request will have a payload looking like this:

{
  "base_currency": "USD",
  "url": "http://some.exampleserver.foo/currency/webhook",
  "token": "somemagickeyword"
}

When new currencies arrive the backend will make a POST request to this URL containing the currency rate payload and have an authorization (Authorization) header set to the contents of token (for security purposes to prevent anyone from POST’ing to that URL).

What’s next?

Coding!

We are going to select a development environment and deployment stack we want to use and then implement this backend.

Continue to day two!