-
Notifications
You must be signed in to change notification settings - Fork 2
Extending Example
Wouter den Bakker edited this page Nov 29, 2019
·
4 revisions
The best way to extend the server is using it as a dependency. This makes it easy to get the latest updates without having to worry about merge conflicts. This tutorial/example will take you through setting up a node.js project with typescript, as well as some examples for creating a custom api. For a full list of all available functions see Extending Functions
- Download and install node.js and yarn.
- Create a folder with:
- A tsconfig.json file, you can copy the file from the Validana Server.
- A package.json file. A minimum file can look like:
{
"name": "myapp",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@types/node": "*",
"@coinversable/validana-server": "^2.1.1"
},
"devDependencies": {
"typescript":"^3.7.0"
}
}
- Run
yarn installin the folder to download the dependencies.
- Create a file 'src/index.ts'. This is the entry point of the program. An example of how it looks like can be found below.
import * as cluster from "cluster";
import { Log, Config, start } from "@coinversable/validana-server";
//Import the class that will take care of incoming api requests. We will create this in a moment.
import { MyHandler } from "./myhandler";
//Optionally, if you want to use Sentry use this to report the version to Sentry.
Log.options.tags.version = require("../package.json").version;
//Want more config options? Add them here and it will load them from the config file/environmental variables.
//First argument is the name of the option, second argument is a default value (if any), third argument a validator function (if any).
Config.addStringConfig("VSERVER_APPNAME", "defaultName", (name) => {
if (name === "") {
throw new Error("Name may not be empty.");
}
});
Config.addNumberConfig("VSERVER_MYFAVORITENUMBER");
//Need to load some files? Do some other setup? Custom setup can go here.
//Note that the server creates worker threads for handling incoming api requests, while the master just ensure they all stay online.
if (cluster.isWorker) {
Log.info("Here is another worker.");
}
//The handler we create will be for version 1 of the api, we make it available at oursite.com/v1
const myApiVersions = new Map();
myApiVersions.set("v1", new MyHandler())
//Finally we start the server. At this point all config values will be loaded.
start(myApiVersions);
//Any further setup you want to do now that the config values have been loaded.
- Create a file 'src/myhandler.ts'. This will contain all requests you can make to the api.
import { addBasics, RequestHandler, Database, Message, Config } from "@coinversable/validana-server";
//Create a custom class that extends RequestHandler. We use addBasics() to add all build-in api queries.
export class MyHandler extends addBasics(RequestHandler) {
constructor() {
super();
//Here we add custom api requests. The first argument is the query to call the api with.
//The second argument is the function that deals with the request.
//The function itself will receive 2 arguments: The data that came with the request,
// and an object containing information about the request and response.
this.addMessageHandler("guessfavoritenumber", this.guessFavoriteNumber);
this.addMessageHandler("getobjects", this.objectsRequest);
}
protected async guessFavoriteNumber(data: number): Promise<string> {
//Validate if data we receive is correct.
if (typeof data === "number") {
//By returning a string instead of an error we notify the server this is a user error.
//This also sets the status code of the response to 400 (instead of 500 on errors).
return Promise.reject("That is not a number.");
}
//Load a config value.
const favNumber = Config.get<any>().VSERVER_MYFAVORITENUMBER;
//We gave it no default or validator requiring it has a value, so it may be undefined
if (favNumber === undefined) {
return "We do not have a favorite number."
} else if (favNumber > data) {
return "Too low.";
} else if (favNumber < data) {
return "Too high.";
} else {
return "Correct.";
}
}
//If you added the example contracts from: https://github.com/Coinversable/validana-processor/wiki/Example-Smart-Contracts
protected async objectsRequest(data: string, message: Message): Promise<any[]> {
if (typeof data !== "string") {
return Promise.reject("Invalid owner.");
}
//Do a parameterized database query.
const dbResult = await Database.get().query("SELECT * FROM objects WHERE owner = $1;", [data]);
if (dbResult.rows.length === 0) {
//We can use the message to overwrite the default status code.
message.statusCode = 404;
return Promise.reject("No objects found for this owner.");
} else {
return dbResult.rows;
}
}
}
- The server assumes a Validana Processor/Node has been set up.
- Create a Config file and fill in the values. You can also add the custom VSERVER_MYFAVORITENUMBER from the example if you want.
- Next build the server with
yarn build. - If everything succeeds you can start the server with
yarn start path/to/config.json. - You can now access the server, for example try http://localhost:8080/v1/guessfavoritenumber?123
- Create a file named 'Dockerfile', see an example below, that will copy the build example server from the same folder:
ARG NODEVERSION=12
FROM node:${NODEVERSION}
ENV NODE_ENV=production
ENV NODE_NO_WARNINGS=1
COPY . /usr/node
# Add any environmental variables here. See the Validana Server Dockerfile.
USER node
WORKDIR /usr/node
ENTRYPOINT ["node", "dist/index.js"]
- To build and start it follow the Setup
Assuming you are running a Validana Processor/Node with docker-compose:
- Shutdown the Processor/Node (See the setup of the processor/node for how.)
- Create a Dockerfile (see above)
- Modify the docker-compose file and change services.server.build.context to this folder. You may need to move the docker-compose file to a parent folder/this folder.
- Alternately remove the services.server entirely, change services.database.expose to services.database.ports and change "5432" to "5432:5432". In this case build, start and stop the Validana Server separately using the docker Setup.
- Start the Processor/Node (See the setup of the processor/node for how.)