Step 2: Building the backend
Now we'll create four typescript files plus a starter data file to hold the code for the backend.
Make sure you're in the src folder. Create a file called index.ts and add the following to it. This holds the main express.js portion of the app.
import express from 'express';
import { sendSampleData, query, createOrUpdateMediaItem, getItemByEntityKey, purge, getBlockNumber } from './dataService.js';
import cors from 'cors';
import { Hex } from 'golem-base-sdk';
import { GOLEM_BASE_APP_NAME, Searches } from './media.js';
import { getSearchEntity } from './searches.js';
const app = express();
const port = 3000;
const corsOptions = {
origin: 'http://localhost:4200'
};
app.use(cors(corsOptions));
app.use(express.json());
app.get('/', (req, res) => {
res.send('Hello from Golem-base!');
});
// Call this to pre-load sample data and build the initial index
app.get('/load-data', async (req, res) => {
await sendSampleData()
res.send({"status": "OK"});
});
// Save a new media item
app.post('/save', async (req, res) => {
let result:any;
// If the request has a key already with it, it's an update.
if (req.body.key) {
result = await createOrUpdateMediaItem(req.body, req.body.key);
}
else {
result = await createOrUpdateMediaItem(req.body);
}
res.send(result);
});
// This can be used to populate dropdown boxes for searches
app.get('/search-options', async (req, res) => {
let searches:Searches = await getSearchEntity();
delete searches.entityKey;
res.send(searches);
});
// The main query entrypoint. This builds a query string and sends it to the query function in the Golem-base SDK.
app.get('/query', async (req, res) => {
// Run through each item; convert nums to numbers
let queryMap:any = {};
for (let key in req.query) {
const value:any = req.query[key];
console.log(value);
console.log(isNaN(value));
if (isNaN(Number(value))) {
queryMap[key] = value;
}
else {
queryMap[key] = Number(value);
}
}
console.log('Building query string');
let queryString:string = `app="${GOLEM_BASE_APP_NAME}"`;
console.log(queryString);
for (let key in queryMap) {
let value:any = queryMap[key];
console.log(value);
if (typeof(value) == 'number') {
queryString += ' && ' + `${key}=${value}`;
}
else {
queryString += ' && ' + `${key}="${value}"`;
}
}
console.log(queryString);
const result:any = await query(queryString);
res.send(result);
})
// Clear out the data.
app.get('/purge', async (req, res) => {
res.send(await purge());
});
// Get the object associated with the provided Hex key. (It needs to be preceded by 0x, which we add if necessary.)
app.get('/key/:id', async (req, res) => {
let id:string = req.params.id;
// Prepend '0x' if it's missing
if (!id.startsWith('0x')) {
id = '0x' + id;
console.log('Updated id with 0x:', id);
}
console.log('Loading by key!');
console.log('id is:', req.params.id);
res.send(await getItemByEntityKey(req.params.id as Hex));
})
// Start server
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
Next, create a file called media.ts and add the following, which is mostly interfaces and types needed throughout the project; as you can see, these are types of media that you can store in the system. Searches refers to the index.
import { Hex } from "golem-base-sdk";
export const GOLEM_BASE_APP_NAME = 'golembase-media_demo_v0.5';
export type MediaType = "book" | "movie" | "music";
export interface Book {
type: "book";
title: string;
description: string;
author: string;
genre: string;
rating: number;
owned: boolean;
year: number;
}
export interface Movie {
type: "movie";
title: string;
description: string;
director: string;
genre: string;
rating: number;
watched: boolean;
year: number;
}
export interface Music {
type: "music";
title: string;
description: string;
artist: string;
genre: string;
rating: number;
favorite: boolean;
year: number;
}
export type MediaItem = Book | Movie | Music;
export interface Searches {
entityKey?: Hex;
directors: string[];
artists: string[];
authors: string[];
movie_genres: string[];
music_genres: string[];
book_genres: string[];
}
Head to Step 3.