Create a Crypto Converter with Svelte and Strapi

Goal

  • understand Svelte, its pros and cons,
  • be able to scaffold a new Svelte project using NPM,
  • understand Svelte components
  • and create the front end of our Cryptocurrency converter using Svelte.

Prerequisite

What is Strapi?

Step 1 — Create a Strapi Project

yarn create strapi-app crypto-converter --quickstart

Crypto Converter API

Step 2 — Create a collection.

  • From the side menu in the dashboard, click on Content-Types Builder
  • Click the “+ Create new collection type” link
  • Name it crypto-converter and click continue
  • Add a Text field (short text) and name it Name
  • Click the “Finish” button
  • Click the Save button

Step 3 — Create a route

strapi generate:controller my-crypto-converter
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services)
* to customize this service
*/
module.exports = {
convert: async ctx => {
await ctx.send({
message: "controller configured"
})
},
};
{
"method": "POST",
"path": "/my-crypto-converter/convert",
"handler": "my-crypto-converter.convert",
"config": {
"policies": []
}
}

Step 4 — Grant public access to the route

Step 5 — Install Axios

yarn add Axios

Step 6 — Create a Nomics.com API key and fetch rates using Axios

https://api.nomics.com/v1/currencies/ticker?key=YOUR_API_KEY&ids=BTC,ETH,XRP,
'use strict';
const axios = require('axios');
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services)
* to customize this service
*/
module.exports = {
convert: async ctx => {
//post values
const amount = ctx.request.body.amount;
const baseCoin = ctx.request.body.baseCoin;
const compareCoin = ctx.request.body.compareCoin;

// fetch all rates in USD
const baseCoinRate = await getRate(baseCoin);
const compareCoinRate = await getRate(compareCoin);
// get rate in crypto currency
const exchange = await calculateExchange(baseCoinRate, compareCoinRate, amount);
return exchange;
},

};
//get exchange rate
async function getRate(coin){
//API key should be passed along as key in the URL below
const { data } = await axios.get(`https://api.nomics.com/v1/currencies/ticker?key=169170*******************90&ids=${coin}`);
return data[0].price;

}
// calculate and return result
async function calculateExchange (base, compare, amount){
const rate = await base/compare;
const result = await rate * amount
return result;
}
};
  • fetch coin values
  • calculation and conversion
  • return result
// fetch all rates in USD
const baseCoinRate = await getRate(baseCoin);
const compareCoinRate = await getRate(compareCoin);
`calculateExchange()` does the calculation and logic. It returns the final result. This function accepts three parameters `baseCoinRate`, `compareCoinRate`, `amount`. This snippet below shows how we used it.
// calculate and return result
async function calculateExchange (base, compare, amount){
const rate = await base/compare;
const result = await rate * amount
return result;
}

What is Svelte?

  • Svelte is a compiler, not a framework. This simply means that Svelte is not shipped with dozens of framework script making it light weighted and easier to learn
  • Compiles code into a single vanilla JavaScript file ready for deployment. Deployment of the compiled file only, not the entire Svelte technology results in a faster deployment process and better performance of our application in production.
  • React, Vue, and the like are shipped with lots of features out of the box. This might not be the case with Svelte as you might have to do a lot of things yourself when using Svelte.

Step 7 — Create a Svelte project

npx degit sveltejs/template crypto-converter-svelte
cd crypto-converter-svelte
npm install
npm run dev
moz-todo-svelte
├── README.md
├── package.json
├── package-lock.json
├── rollup.config.js
├── .gitignore
├── node_modules
├── public
│ ├── favicon.png
│ ├── index.html
│ ├── global.css
│ └── build
│ ├── bundle.css
│ ├── bundle.js
│ └── bundle.js.map
├── scripts
│ └── setupTypeScript.js
└── src
├── App.svelte
└── main.js
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
name: 'Moses'
}
});
export default App;
<script>
export let name;
</script>
<main>
<h1>Hello {name}!</h1>
<p>
Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn
how to build Svelte apps.
</p>
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
h1 {
color: #ff3e00;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
<script>
import Modal, { getModal } from "./Modal.svelte";
let amount;
let baseCoin;
let convertCoin;
let result;
function handleSubmit(e) {
conversion(amount, baseCoin, convertCoin);
}
async function conversion(amount, baseCoin, convertCoin) {
const call = await fetch(
"http://localhost:1337/my-crypto-converter/convert",
{
method: "POST",
body: JSON.stringify({
baseCoin: baseCoin,
compareCoin: convertCoin,
amount: amount,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
}
);
result = await call.json();
//opens modal
getModal().open();
}
</script>
<div class="container">
<h1>Crypto converter</h1>
<form id="surveyForm" class="mt-4" on:submit|preventDefault={handleSubmit}>
<div class="form-group">
<label for="baseCoin">Choose base currency:</label>
<select name="baseCoin" bind:value={baseCoin}>
<option value="BTC">Bitcoin</option>
<option value="ETH">Ethereum</option>
<option value="XRP">Ripple</option>
<option value="audi">Audi</option>
</select>
</div>
<div class="form-group">
<label for="compareCoin">Choose currency to convert to:</label>
<select name="compareCoin" bind:value={convertCoin}>
<option value="BTC">Bitcoin</option>
<option value="ETH">Ethereum</option>
<option value="XRP">Ripple</option>
<option value="audi">Audi</option>
</select>
</div>
<div class="form-group">
<input
type="num"
class="form-control"
placeholder="Enter amount in coin"
bind:value={amount}
required
/>
</div>
<button class="btn btn-full">Convert</button>
</form>
<Modal>
<h4>Success!!</h4>
<h3>
<small>You will get</small>
{result} <small>in</small>
{convertCoin}
</h3>
</Modal>
</div>
<!-- the modal without an `id` -->
<style>
.container {
max-width: 1200px;
margin: 0 auto;
margin-top: 8rem;
}
h2 {
margin-top: 0;
}
.form-group > *,
.btn-full {
width: 100%;
}
.form-control,
.btn-full {
border-radius: 3px;
}
.submitted input:invalid {
border: 1px solid #c00;
}
.submitted input:focus:invalid {
outline: 1px solid #c00;
}
.error-alert {
border: 1px solid #c00 !important;
padding: 6px;
text-align: center;
color: #c00;
border-radius: 3px;
}
</style>
<Modal>
<h4>Success!!</h4>
<h3>
<small>You will get</small>
{result} <small>in</small>
{convertCoin}
</h3>
</Modal>
<script context="module" lang="ts">
let onTop; //keeping track of which open modal is on top
const modals = {}; //all modals get registered here for easy future access
// returns an object for the modal specified by `id`, which contains the API functions (`open` and `close` )
export function getModal(id = "") {
return modals[id];
}
</script>
<script lang="ts">
import { onDestroy } from "svelte";
let topDiv;
let visible = false;
let prevOnTop;
let closeCallback;
export let id = "";
function keyPress(ev) {
//only respond if the current modal is the top one
if (ev.key == "Escape" && onTop == topDiv) close(); //ESC
}
/** API **/
function open(callback) {
closeCallback = callback;
if (visible) return;
prevOnTop = onTop;
onTop = topDiv;
window.addEventListener("keydown", keyPress);
//this prevents scrolling of the main window on larger screens
document.body.style.overflow = "hidden";
visible = true;
//Move the modal in the DOM to be the last child of <BODY> so that it can be on top of everything
document.body.appendChild(topDiv);
}
function close(retVal) {
if (!visible) return;
window.removeEventListener("keydown", keyPress);
onTop = prevOnTop;
if (onTop == null) document.body.style.overflow = "";
visible = false;
if (closeCallback) closeCallback(retVal);
}
//expose the API
modals[id] = { open, close };
onDestroy(() => {
delete modals[id];
window.removeEventListener("keydown", keyPress);
});
</script>
<div id="topModal" class:visible bind:this={topDiv} on:click={() => close()}>
<div id="modal" on:click|stopPropagation={() => {}}>
<svg id="close" on:click={() => close()} viewBox="0 0 12 12">
<circle cx="6" cy="6" r="6" />
<line x1="3" y1="3" x2="9" y2="9" />
<line x1="9" y1="3" x2="3" y2="9" />
</svg>
<div id="modal-content">
<slot />
</div>
</div>
</div>
<style>
#topModal {
visibility: hidden;
z-index: 9999;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #4448;
display: flex;
align-items: center;
justify-content: center;
}
#modal {
position: relative;
border-radius: 6px;
background: white;
border: 2px solid #000;
filter: drop-shadow(5px 5px 5px #555);
padding: 1em;
}
.visible {
visibility: visible !important;
}
#close {
position: absolute;
top: -12px;
right: -12px;
width: 24px;
height: 24px;
cursor: pointer;
fill: #f44;
transition: transform 0.3s;
}
#close:hover {
transform: scale(2);
}
#close line {
stroke: #fff;
stroke-width: 2;
}
#modal-content {
max-width: calc(100vw - 20px);
max-height: calc(100vh - 20px);
overflow: auto;
}
</style>

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Strapi

Strapi

The open source Headless CMS Front-End Developers love.