29
loading...
This website collects cookies to deliver better user experience
npx create-strapi-app strapi-api --quickstart
# OR
yarn create strapi-app strapi-api --quickstart
yarn build
to build your app and yarn develop
to run the new project if it doesn't start automatically.crypto
that will have feilds like name
, price
, alert_price
.Collection Type Builder
on the left side of the page. Click on create New Collection Type
still at the left side of the page and fill in Crypto
as the display name.Continue
to create a new Crypto
collection. If you noticed, I have created other collections. That's the flexibility of Strapi.Crypto
collection with lots of Crypto data. You can achieve this in two ways: using the Admin UI and using Strapi generated API.Continue
, and it will present you with another modal to select fields for your Collection Type.Text
and fill in Name
at the Text field. Click on Add another field
and select Number
(float type) for the price
and alert_price
fields.Save
to save the collection and click on the Crypto
name on the left side.Add new crypto
button to add a new crypto currency. We will add Bitcoin as our test crypto currency because we know the current price of BTC. You can add any crypto currency of your choice and click on the Save and Publish buttons afterward.npx create-next-app nextjs-crypto-stats-app --use-npm --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"
nextjs-crypto-stats-app
using NPM as the build tool and the learn-starter
as the example template.cd nextjs-crypto-stats-app
npm run dev
localhost:3000
.components
and create the following file inside.Crypto.js
file and paste in the following code:import React, { useState } from "react";
import Modal from "./Add";
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}
export default function Crypto({ crypto }) {
const [showModal, setShowModal] = useState(false);
return (
<div className="card" onClick={() => setShowModal(true)}>
<h3>{crypto.id} →</h3>
<p>${crypto.price}</p>
{showModal ? (
<Modal
onClose={() => setShowModal(false)}
show={showModal}
crypto={crypto}
></Modal>
) : null}
<div id="modal-root"></div>
<style jsx>{`
.card {
margin: 1rem;
flex-basis: 10%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
div.StyledModalHeader {
display: flex;
justify-content: flex-end;
font-size: 25px;
}
input[type="text"],
select,
textarea {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
}
button {
background-color: #04aa6d;
color: white;
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
float: right;
}
button {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
}
div.StyledModal {
background: white;
width: 300px;
height: 400px;
border-radius: 15px;
padding: 15px;
}
div.StyledModalOverlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
}
`}</style>
</div>
);
}
components
directory called Add.js
and paste in the following code. You will also have to install react-modal:npm i react-modal
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Modal from "react-modal";
import { storeAlertPrice } from "../lib/Nomics";
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
},
};
Modal.setAppElement("#modal-root");
function Add({ show, crypto }) {
let subtitle;
const [modalIsOpen, setIsOpen] = useState(show);
const [price, setPrice] = React.useState(0);
const [isBrowser, setIsBrowser] = useState(false);
useEffect(() => {
setIsBrowser(true);
}, []);
function afterOpenModal() {
subtitle.style.color = "#f00";
}
function closeModal() {
setIsOpen(false);
}
const modalContent = modalIsOpen ? (
<div>
<Modal
isOpen={modalIsOpen}
onAfterOpen={afterOpenModal}
onRequestClose={closeModal}
style={customStyles}
contentLabel="Modal"
>
<button onClick={closeModal}>close</button>
<h2 ref={(_subtitle) => (subtitle = _subtitle)}>Enter your price</h2>
<form
onSubmit={async (e) => {
e.preventDefault();
console.log(price);
await storeAlertPrice(crypto, price);
}}
>
<input
name="price"
value={price}
onChange={(e) => setPrice(e.target.value)}
type="text"
/>
<button type="submit">Set Price</button>
</form>
</Modal>
</div>
) : null;
if (isBrowser) {
return ReactDOM.createPortal(
modalContent,
document.getElementById("modal-root")
);
}
return null;
}
export default Add;
CryptoList.js
and paste in the following code.import Crypto from "../components/Crypto";
export default function Cryptos({ cryptos }) {
return (
<div className="grid">
{cryptos.map((crypto) => (
<Crypto crypto={crypto} key={crypto.id} />
))}
<style jsx>{`
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 1000px;
margin-top: 1rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
</div>
);
}
index.js
file in pages/index.js
folder and replace it with the following code.import Head from "next/head";
import { useEffect } from "react";
import Cryptos from "../components/CryptoList";
import { checkAlertPrice, getCryptoData } from "../lib/Nomics";
export async function getStaticProps() {
const cryptos = await getCryptoData();
return {
props: {
cryptos,
},
};
}
export default function Home({ cryptos }) {
useEffect(() => {
window.setInterval(async function () {
const alertArray = await checkAlertPrice();
if (alertArray.length) alert(alertArray.map((item) => item));
}, 60000);
});
return (
<div className="container">
<Head>
<title>Crypto Alerta</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">
Welcome to <a href="https://nextjs.org">Crypto Alerta!</a>
</h1>
<p className="description">
Get started by clicking on each crypto currency, and adding the amount
you want to be notified
</p>
<Cryptos cryptos={cryptos} />
</main>
<footer>
<div>Crypto Alerta</div>
</footer>
<style jsx>{`
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
footer img {
margin-left: 0.5rem;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
a {
color: inherit;
text-decoration: none;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
}
.logo {
height: 1em;
}
`}</style>
<style jsx global>{`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
sans-serif;
}
* {
box-sizing: border-box;
}
`}</style>
</div>
);
}
nom i axios
lib/Nomics.js
in the root directory and paste in the following scripts.import axios from "axios";
const endpoint = `https://api.nomics.com/v1/currencies/ticker?key=YOUR_API_KEY&ids=BTC,ETH,XRP,SHIB,ADA,YFI,DOGE,CKB,DOT,SUSHI.BTT,DENT,MATIC,CHZ&interval=1d,30d&convert=USD&per-page=100&page=1`;
export async function getCryptoData() {
const res = await axios.get(endpoint);
const cryptos = res.data;
await storeOrUpdate(cryptos);
return cryptos;
}
async function storeOrUpdate(cryptos) {
for (const key in cryptos) {
if (Object.hasOwnProperty.call(cryptos, key)) {
const newCrypto = cryptos[key];
const crypto = await get(newCrypto.id);
if (crypto) {
// Update
await newUpdate(crypto.id, newCrypto);
} else {
//Store
await store(newCrypto);
}
}
}
}
async function store(data) {
const newData = {
price: data.price,
name: data.id,
};
const res = await axios.post("http://localhost:1337/cryptos", newData);
return res.data;
}
async function newUpdate(id, data) {
const newData = {
price: data.price,
name: data.id,
};
await update(id, newData);
}
async function updateAlertPrice(id, price) {
const newData = {
alert_price: price,
};
const crypto = await get(id);
await update(crypto.id, newData);
}
async function update(id, data) {
const res = await axios.put(`http://localhost:1337/cryptos/${id}`, data);
return res.data;
}
async function get(name) {
const res = await axios.get(`http://localhost:1337/cryptos/names/${name}`);
if (res.data.success) {
return res.data.crypto;
}
return null;
}
export async function storeAlertPrice(crypto, alertPrice) {
// Store to local storage
localStorage.setItem(crypto.id, alertPrice);
//Upate to Strapi
await updateAlertPrice(crypto.id, alertPrice);
return;
}
async function isSamePrice(crypto) {
// Check localStorage prices
let alertPrice = localStorage.getItem(crypto.id);
if (parseFloat(alertPrice) >= parseFloat(crypto.price)) {
return true;
}
// Check Strapi prices
const strCrypto = await get(crypto.id);
if (parseFloat(strCrypto.alert_price) >= parseFloat(crypto.price)) {
return true;
}
return false;
}
export async function checkAlertPrice() {
//Load new Crypto prices
const cryptos = await getCryptoData();
const alertArr = [];
for (const key in cryptos) {
if (Object.hasOwnProperty.call(cryptos, key)) {
const crypto = cryptos[key];
// Check Prices
if (await isSamePrice(crypto)) {
alertArr.push(
`${crypto.id} has reached the ${crypto.price} amount you set`
);
}
}
}
return alertArr;
}
YOUR_API_KEY
with your real API key gotten from the Nomics account and specify the names of all the Cryptocurrencies you want to retrieve their prices.api/cryptos/config/routes.js
, and add the following code. The code will create a new route in your Strapi backend to find a single crypto with the crypto name.//....
{
"method": "GET",
"path": "/cryptos/names/:name",
"handler": "cryptos.findOneByName",
"config": {
"policies": []
}
},
//....
cryptos.js
at api/crypto/controllers/cryptos.js
and add the following code. The code below impliments the logic of finding a single crypto from our Strapi collection using the route we defined above."use strict";
const { sanitizeEntity } = require("strapi-utils");
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
* to customize this controller
*/
module.exports = {
async findOneByName(ctx) {
const { name } = ctx.params;
const entity = await strapi.query("cryptos").findOne({ name: name });
if (entity)
return ctx.send({
message: "Crypto found",
success: true,
crypto: sanitizeEntity(entity, { model: strapi.models.cryptos }),
});
return ctx.send({
message: "Crypto not found",
success: false,
});
},
};
Settings
item on the sidebar menu, then on the Roles
item on the second sidebar menu that appears. On the right section, click on the Public
item and scroll down.Select all
checkbox and click on the Save
button at the top. This settings will allow public access to all the Crypto APIs in our Strapi project.