Features - serverless Node.js backend
Although codehooks.io Node.js backend has many and seemingly advanced features, they are easy to understand and use. We have identified what we believe are the essential building blocks of a modern serverless backend and tied them together with instantly deployable Node.js JavaScript serverless backend code managed by an easy to use CLI tool and Admin GUI.
Codehooks fits perfectly as a Node.js backend for React, Vue, Svelte or <insert favorite frontend framework>. It can also be a good fit as a tool for creating APIs for collecting data from IoT devices or create quick automations and integrations.
- Serverless Node.js backend with fast REST API similar to ExpressJS
- NoSQL document database with MongoDB-like queries
- Fast Key-Value store
- Simple Worker Queues
- Scheduled background jobs controlled by CRON expressions
- Easy Authentication of APIs using JWKS
- Open source option using Express and MongoDB to prevent lock-in
Serverless Node.js backend with fast REST API similar to Express
Use the power of JavaScript (or TypeScript) and Node.js NPM just like in any ExpressJS project. Directly within the API handler functions, you have access to the NoSQL document database, the Key-Value store and the Worker Queues (see the description of these features below). The combination of these features lets you easily build sophisticated data processing logic for your APIs with very little code and setup. The entire backend application is deployed with a single command
coho deploy
, which only takes a few seconds.// ...
// examples of all handlers available for API creation
app.use(fooMiddleware); // global middleware - intercept calls like like ExpressJS
app.get('/ping', (req, res) => {
res.json({ "message": "Pong!"});
});
app.get('/foo', getFooFunc); // HTTP GET event handler
app.get('/foo/:id', getOneFooFunc); // HTTP GET event handler(one)
app.post('/foo', postFooFunc); // HTTP POST event handler
app.put('/foo/:id', putFooFunc); // HTTP PUT event handler
app.patch('/foo/:id', patchFooFunc); // HTTP PATCH event handler
app.delete('/foo/:id', deleteFooFunc); // HTTP DELETE event handler
app.all('/foo', allFooFunc); // handle all GET, POST, PUT, PATCH, DELETE
app.auth('/foo', fooMiddlewareFunc); // custom auth for API route /foo
export default app.init();
NoSQL document database with MongoDB-like queries
The NoSQL JSON document database is available inside the JavaScript serverless backend functions directly without any connection string or setup. The database is powered by the open source database engine RocksDB, providing a blazing fast performance even for huge datasets (using indexes). We provide a MongoDB-like syntax to query the database and also convenience methods to easily stream data through the serverless backend API endpoints. Data can be imported and exported from/to JSON and CSV formats using the CLI.
import { app, Datastore } from 'codehooks-js'
async function updateDevice(req, res) {
const conn = await Datastore.open();
// TODO: validate input ;-)
const data = await conn.updateOne('devices', req.params.id, req.body);
res.json(data);
}
// .../device?search=raspberry
async function findDevices(req, res) {
const conn = await Datastore.open();
const { search } = req.query;
const filter = {"name" : {$regex : `${search}`, $options: "i"}}
const options = {
limit: 100,
filter,
}
conn.getMany('devices', options).json(res); // stream result
}
// HTTP API routes and handlers
app.patch('/device/:id', updateDevice);
app.get('/device', findDevices);
export default app.init();
Fast Key-Value store
Sometimes you need to store and lookup data using custom keys, not database ids like in a NoSQL document database. A data cache, a JWT token or user session, a counter... the use cases are many. We have embedded a simple key-value store which lets you easily persist data. The values can be retrieved with single keys or using a key pattern to fetch multiple values. Using key patterns, you can also stream the result through an API endpoint.
import { app, Datastore } from 'codehooks-js'
app.get('/ticket/:id', async (req, res) => {
const conn = await Datastore.open();
const { id } = req.params;
// get the ticket from the key-value store
const ticket = await conn.get(`ticket-${id}`);
if (!ticket) { return res.status(404).json({ error: 'Ticket not found'});}
// increase counter by 1
const retrieveCount = await conn.incr(`ticket-count-${id}`, 1);
res.json({ ticket, retrieveCount });
});
app.post('/ticket', async (req, res) => {
const { transactionid, email } = req.body;
const conn = await Datastore.open();
const ticket = await someNiceFunctionForCreatingATicket(transactionid, email); // implemented elsewhere ;)
// store the ticket in the key-value store
conn.set(`ticket-${ticket.id}`, ticket);
res.status(201).json({ ticket });
});
export default app.init();
Simple Worker Queues
Backends sometimes need to process data asynchronously to handle longer running tasks. By using a persistent queue, an API call can return immediately and let the queue worker do it's job at its own pace. For instance, an API call could receive the order to create a ticket PDF and then put the job of creating the ticket-PDF into a queue and return an OK response to the web client. When the queue worker script is finished it would send out an email with the ticket.
import { app, Datastore } from 'codehooks-js'
app.worker('ticketToPDF', (req, res) => {
const { email, ticketid } = req.body.payload;
//TODO: implement code to fetch ticket data, produce PDF
// and send email with attachment using Mailgun, Sendgrid or Amazon SES
// ...and implement error handling
res.end(); // done processing queue item
})
app.post('/createticket', async (req, res) => {
const { email, ticketid } = req.body;
const conn = await Datastore.open();
await conn.enqueue("ticketToPDF", { email, ticketid });
res.status(201).json({"message": `Ticket will be generated and sent to ${email}`});
})
export default app.init();
Scheduled background jobs controlled by CRON expressions
When you need to run some code at specific times and intervals, you need a scheduled background job. It's basically just a function which runs whenever you want it to, specified by a CRON expression. Let's say you need to send out a newsletter each morning at 8AM or you need to process the transactions each evening at 9PM or you need to scan a webpage every 5 minutes for some updates. A job hook is your go to tool for these kinds of...eh...jobs.
import { app, Datastore } from 'codehooks-js'
import * as cheerio from 'cheerio';
import fetch from 'node-fetch';
const scrapePlayerScoringStats = async () => {
// TODO: implement the web scraper using cheerio
return result;
}
// run every evening at 23:00 (11pm)
// use https://crontab.guru to learn more about CRON
app.job('0 23 * * *', async (req, res) => {
const playerStats = await scrapePlayerScoringStats();
const conn = await Datastore.open();
for(let result of playerStats){
conn.set(`player-${result.player}`, result.goals);
}
})
Easy Authentication of APIs using JWKS
How do you authenticate an API? In modern web applications, users get redirected to a login page to prove their identity using a social provider or username/password. They are then redirected back and the application receives some form of access token which is exchanged with a JWT token for use with the application. This token is then sent with each API call and must be verified by the backend. With codehooks.io, the backend developer can add a JWKS endpoint and codehooks.io will verify the tokens for you. You can override authentication using the `app.auth` event handler if you need to.
