This example provides the steps to build a simple API using hyper and NodeJS. It includes:
hyper Data API๏ปฟ : a scalable database solution with a clean separation from business logic and service implementation
hyper Cache API๏ปฟ : a scalable cache solution that gives JSON document Key Value features with Time To Live functionality
hyper Search API ๏ปฟ : a scalable search solution that makes it as simple as a click to spin up a search index
๏ปฟZod: a data validation utility that empowers the development team to manage their data model in the business layer of the application
๏ปฟexpressJS: a nodejs application server framework
By the end of this workshop, you'll have an API service that allows users to create, update, list, view, search and delete video game documents.
hoppscotch showcasing workshop
๏ปฟ
Github
Whenever you get stuck at any point, take a look at this repo
Project setup
Before we get started we need to setup our hyper services, This is as simple as logging into the hyper dashboard and clicking add application - You can name the application whatever you like (ie. [initials]-video-game-api) and make sure that both the data and cache services are selected. Awesome!
Get the API Key
When the app is created, copy the Connection String on the keys tab, by clicking the copy icon to the right of the string.
Create a NodeJS/Express project
๐ You will need to have nodejs >= v14 installed and access to a code editor like visual studio code, and a command-line terminal window. You can download https://nodejs.org๏ปฟ
In a command-line terminal window type the following commands:
โ If you have any problems installing the above tools, please reach out to our community slack. You can register at https://hyper.io/slack and go to the #getting startedchannel, and someone will help you out.
Shell
npm start
๏ปฟ
Building the App
Let's start building our API from scratch!
Create Express App
Open the server.js file in your code editor and type the following:
JS
import express from'express'import cors from'cors'import{ connect }from'hyper-connect'const app =express()const hyper =connect(process.env.HYPER)
app.use(cors())
app.get('/',function(req, res){
res.send('Video Game API')})
app.listen(3000)
console.log('API Server Running on port 3000')
๏ปฟ
Open your browser to port 3000 and you should see:
Open hoppscotch in a new browser tab and set the method to be GET and enter in your server address: http://localhost:3000 - then click send - you should see the result 'Video Game API' in the response body.
Creating a Video Game Document
create a new file called Game.js - This file will contain our document schema, and a validation function
JS
import{ z }from'zod'const Game = z.object({_id: z.string(),type: z.literal('game'),name: z.string(),company: z.string(),released: z.string().min(4).max(4).regex(/^([0-9]){4}$/)})/**
* @param {Game} data
*/exportfunctionvalidate(data){return Game.parseAsync(data)}
๏ปฟ
๐ This functionality is not hyper-related, but you always want to validate any data coming from a client or a service. (Soapbox ๐งผ ๐ฆ ) Never trust the shape of the data and don't depend on your database to validate the shape, you will want to add custom business logic and you don't want to add this logic to your data service, it will create a maintenance nightmare over time.
Create API Endpoint to create a video game
In the server.js file lets create new express handler
๐ The POST /games endpoint, uses hyper.data.add method to submit the game document to the data store, the validate method makes sure the document is the correct shape, but it could be a duplicate, this is where the response from hyper comes in, if the document _id already exists, this function will return a 409 error, document conflict.
You will want to add the API handler after the const declarations, but before the app.get handler.
Using hoppscotch, send a POST command to http://localhost:3000/games URL. It should return back a status of 500 and an error, showing that several elements are required!
Now, use the body tab in the hoppscotch UI to create our JSON document
๐ This incrementCounter utility shows how composable hyper's API is, we are using a Promise chain to create a functional pipeline passing one result from one call to the other, until we get the final result. If for any reason an error occurs in this pipeline, it should prevent the rest of the pipeline from continuing. To create an even safer ADT, check out the Async module from the Crocs Library.
In the server.js let's modify the promise chain in the app.post handler
๐ here we just add and extra chain method in the promise pipeline, called incrementCounter this method is a generic utility method that will increment any type proved by the caller, so if we add other models to our application, we can take advantage of this re-usable module to keep count stats on all models.
Using the hyper-vision tool, we can look at our data and cache services: https://vision.hyper.io - paste the connection string from the .env file, and go to the cache section, you should see a game-counter key, and the value should be { count: 1 }
โก๏ธ๐ถ hyper-vision is a data explorer for your hyper services, you can checkout data, cache, and search services, it is a simple troubleshooting tool to verify you have data available in your system.
Get a game document by id
Its time to create the get document by id endpoint, GET /games/:id
`server.js`
create a new API handler underneath the post handler:
๐ Using hyper.data.get we request the game document from the data service and validate that the document is correct, before we send it to the client. Just like incoming requests, we do not want to trust the data from the data store, we want to make sure it matches our document shape and specifications.
Using hoppscotch lets get a game document, modify the method to GET and change the URL to http://localhost:3000/games/game-1 then click on the send button.
server.js NOTE: make sure you add this above the app.get('/games/:id') handler
JS
app.get('/games/count',function(req, res){
hyper.cache.get('game-counter').then(result=> result.count ? res.send(result): Promise.reject(result)).catch(err=> res.status(err.status ||500).send(err))})...
app.get('/games/:id',...)// count endpoint should be above of the get doc endpoint
๏ปฟ
๐ Using hyper.cache.get to get the game-counter value from the cache, and check if we got the count property, and handle the response to the caller.
Using hoppscotch call the new endpoint GET /games/count
Response
JSON
{"count":1}
๏ปฟ
Update Game Document
Clients should be able to update Game documents, so lets make that happen, by creating a new API endpoint
๐ The hyper.data.update command takes an _id and a document, in this implementation, we validate the incoming data, then we chain it to the hyper.data.update method, then chain the result and if successful return the successful response, otherwise reject it and return an error response.
Using hoppscotch lets update a game document, by setting the method to PUT and the URL to http://localhost:3000/games/game-1 and the body to the following and click the `send` button: