The Application Object
The Application object creates events that lets you hook your serverless functions accordingly.
Available event types are:
- REST API route events
- Background Job cron and scheduled events
- Worker Queue events
- Authentication events
The event hooks is an Node.js Express style JavaScript library in a public npm library that you install as a npm package in your project directory. You can check for updates in the npm version.
Install the CLI.
npm i codehooks-js
Deploy your app with the CLI command.
coho deploy
API quick overview
- Complete code example
- app.init()
- app.use(workerFunction)
- app.use(route, workerFunction)
- app.get(route, ...middleware?, workerFunction)
- app.post(route, ...middleware?, workerFunction)
- app.put(route, ...middleware?, workerFunction)
- app.patch(route, ...middleware?, workerFunction)
- app.delete(route, ...middleware?, workerFunction)
- app.all(route, ...middleware?, workerFunction)
- app.auth(route, workerFunction)
- app.worker(name, workerFunction, options)
- app.job(cronExpression, workerFunction)
- app.static(options, callback)
- app.storage(options, callback)
- app.crudlify(schema, options)
- app.openapi(config, swaggerPath?)
- openapi(spec) middleware
- app.createWorkflow(name, desc, workflowJSON, config?)
- app.set(key, val)
- app.internalFetch(url, options)
Complete code example
Import the app library in your application and hook into the various events.
import { app, openapi } from 'codehooks-js';
// generic serverless function
function fooFunc(req, res) {
res.end();
}
// generic middleware function
function fooMiddleware(req, res, next) {
next();
}
/*
* Hook into events
*/
app.use(fooMiddleware); // global middleware
app.use('/foo', fooMiddleware); // global middleware on routes
app.get('/foo', fooFunc); // GET
app.post('/foo', fooFunc); // POST
app.put('/foo', fooFunc); // PUT
app.patch('/foo', fooFunc); // PATCH
app.delete('/foo', fooFunc); // DELETE
app.all('/foo', fooFunc); // GET, POST, PUT, PATCH, DELETE
app.auth('/*', fooMiddleware); // Called before outher routes without access tokens
app.job('* * * * *', fooFunc); // subscribe to cron events
app.worker('workername', fooFunc); // subscribe to worker events
app.static(options); // serve deployed static files and content
app.storage(options); // serve dynamic uploaded files and content
app.openapi(config); // OpenAPI docs and Swagger UI
app.crudlify(options); // Database CRUD REST API
// Bind events and functions to the serverless runtime
export default app.init();
app.init()
Mandatory call to bind your functions to events from the serverless runtime.
Parameters none
app.use(workerFunction)
Adds global middleware to all route API events (get, put, post, patch, delete).
Parameters
- workerFunction: function(requestObject, responseObject, next)
Returns void
Code example
import { app } from 'codehooks-js';
import cookieParser from 'cookie-parser';
// external npm lib middleware
app.use(cookieParser());
app.get('/foo', function (req, res) {
// Cookies that have not been signed
console.log('Cookies: ', req.cookies);
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies);
// Custom middleware result
console.log('Foo: ', req.foo);
res.send('Done');
});
export default app.init();
app.use(route, workerFunction)
Adds global middleware to a specific route API events (get, put, post, patch, delete).
Parameters
- route: String or RegExp that matches route
- workerFunction: function(requestObject, responseObject, next)
Returns void
Code example
import { app } from 'codehooks-js';
// custom
app.use('/foo', (req, res, next) => {
console.log('Called before any other route handlers');
req.foo = 'Foo was here!';
next(); // must be called to continue
});
app.get('/foo', function (req, res) {
// Custom middleware result
console.log('Foo: ', req.foo);
res.send('Done');
});
export default app.init();
app.get(route, ...middleware?, workerFunction)
Execute workerFunction function on HTTP GET method and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.get('/foo', function (req, res) {
res.send('Done');
});
export default app.init();
app.post(route, ...middleware?, workerFunction)
Execute workerFunction function on HTTP POST method and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.post('/foo', function (req, res) {
console.log('Data from client is', req.body);
res.send('Done');
});
export default app.init();
app.put(route, ...middleware?, workerFunction)
Execute workerFunction function on HTTP PUT method and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.put('/foo', function (req, res) {
console.log('Data from client is', req.body);
res.send('Done');
});
export default app.init();
app.patch(route, ...middleware?, workerFunction)
Execute workerFunction function on HTTP PATCH method and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.patch('/foo', function (req, res) {
console.log('Data from client is', req.body);
res.send('Done');
});
export default app.init();
app.delete(route, ...middleware?, workerFunction)
Execute workerFunction function on HTTP DELETE method and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.delete('/foo/:ID', function (req, res) {
const { ID } = req.params;
console.log('Delete this', ID);
res.send('Done');
});
export default app.init();
app.all(route, ...middleware?, workerFunction)
If no other routes matched before this, then execute the workerFunction function on all HTTP methods and route match.
Parameters
- route: String or RegExp that matches route
- middleware: (optional) One or more middleware functions, e.g.
openapi(spec), that process the request before the handler - workerFunction: function(requestObject, responseObject)
Returns void
Code example
import { app } from 'codehooks-js';
app.get('/foo', function (req, res) {
res.send('Done GET');
});
app.all('/*', function (req, res) {
res.send(
`None of the above routes matched but this ${req.method} ${req.originalUrl}`
);
});
export default app.init();
app.auth(route, workerFunction)
Execute workerFunction function when no authentication token (x-apikey or JWT) is present. Works for all HTTP methods and route matches.
Parameters
- route: String or RegExp that matches route
- workerFunction: function(requestObject, responseObject)
Returns void
Code example
app.worker(name, workerFunction, options)
Register a named workerFunction function that can be used to process queued events or scheduled events.
Parameters
- name: unique worker name
- workerFunction: function(queueData, responseFunction)
- options: (optional) Object with configuration settings
- workers: number - Number of parallel workers to process queue items concurrently. Limited by your plan quota.
- timeout: number (milliseconds) - Paid plans can set the worker function execution time up to 10 minutes.
Returns void
Code example for worker queues
Code example for scheduled workers
app.job(cronExpression, workerFunction)
Execute workerFunction function when cron job are scheduled.
Parameters
- cronExpression: a valid cron expression string
- workerFunction: function(jobData, responseFunction)
app.static(options, callback)
Serve static files from deployed source directory.
For example: app.static({ route: '/', directory: '/dist', default: 'index.html', notFound: '/index.html' })
Options
- route: URL sub route for clients
- directory: path to file upload directory
- default: (optional) default file to serve when requesting a directory (e.g., 'index.html')
- notFound: (optional) file to serve for 404 errors (essential for client-side routing in SPAs)
- headers: (optional) response headers to set on the files this route serves, e.g.
{ 'Cache-Control': 'public, max-age=31536000, immutable' }. A value ofnullremoves a header instead of setting it.
Cache headers
Static files are served without the platform's default no-store cache headers, so you decide the caching policy per route with headers. The only default is the SPA shell — the default file served for a directory request and the notFound file — which gets Cache-Control: no-cache so a cached shell can't keep pointing at hashed files that no longer exist. Override it with headers like any other value.
headers applies to every file the route serves, so use one route per policy. Route dispatch is first match wins, which means specific routes go before a catch-all:
// Hashed build output — safe to cache forever
app.static({
route: '/assets',
directory: '/dist/assets',
headers: { 'Cache-Control': 'public, max-age=31536000, immutable' },
});
// SPA shell last: catches everything else, revalidates by default
app.static({
route: '/',
directory: '/dist',
default: 'index.html',
notFound: '/index.html',
});
Fast static serving
A static route declared without a callback is served without starting your serverless runtime, which makes it noticeably faster than a route that has one. Those responses carry an x-codehooks-static: hit header, so you can check which routes qualify:
curl -sI https://<your-space>/assets/app.js | grep x-codehooks-static
Only paths that resolve to an actual file are served this way. Anything else — including your API routes, app.realtime() endpoints, and the SPA fallback — runs your functions as usual.
Prefer headers over a callback for cache policy: setting headers is the common reason to add a callback, and a callback opts the route out of fast serving.
Fast static serving requires codehooks-js 1.4.6 or later. 1.4.10 added the no-cache default for the SPA shell, headers: { X: null } removal, and made a callback's own headers survive.
callback(req, res, next)
Provide a middleware function to control the request — logging, redirects, access checks, or anything else that needs to run per request.
app.static({ route: '/assets', directory: '/assets' }, (req, res, next) => {
console.log('Serving a static resource', req.path);
next();
});
Call next() to serve the file. To block a request, respond from the callback instead — that way you pick the status and the body:
app.static({ route: '/private', directory: '/private' }, (req, res, next) => {
if (!isAllowed(req)) {
return res.status(403).end('Forbidden');
}
next();
});
Passing a string to next() — next('not allowed') — also stops the file from being served, but the client gets a generic 404 Cannot GET /path and the string is not sent to it.
app.storage(options, callback)
Serve files from the blob storage file system. You can upload files to the blob storage with the CLI. Also see the Filesystem API docs.
$ coho file-upload --projectname 'myproject-fafc' --space 'dev' --src '/mylocaldir/' --target '/mydocuments'
For example: app.storage({ route: '/docs', directory: '/mydocuments' })
Options
- directory: path to file upload directory
- route: URL sub route for clients
callback(req, res, next)
Provide a middleware function to control the request.
app.crudlify(schema, options)
Creates a complete Database REST API (detailed docs here).
Example: REST API for two collection customer and product, any schema is allowed:
app.crudlify({ customer: {}, product: {} }, { prefix: '/crudapi' });
// Example valid URL's
// https://myproject-ff00.api.codehooks.io/dev/customer?name=Bill
// https://myproject-ff00.api.codehooks.io/dev/product?price>42
Schema
-- collection: schema, for specific collections, e.g. {"collection": someSchema}
Options
- prefix: serve REST routes under another route.
{"prefix": "/api"}
Returns Promise to before<VERB> after<VERB> functions.
Example:
crudlify(app, {product, customer}).then((hooks) => {
hooks.beforePOST('customer', async (data) => {
// do something here
data.foo = 'Was here before saving!'
})
hooks.afterPOST('product', async (data) => {
console.log("Data as saved to the database", data)
// do something here
})
app.openapi(config, swaggerPath?)
Enable automatic OpenAPI 3.0 documentation generation and serve an interactive Swagger UI. When combined with crudlify, schemas are automatically converted to OpenAPI specifications. See the complete OpenAPI documentation for full details.
Parameters
- config (object): OpenAPI configuration object
- info (object, required): API metadata with
title,version, and optionaldescription - servers (array, optional): Server URLs for different environments
- tags (array, optional): Tags to organize endpoints
- filter (function, optional): Filter which operations appear in docs
(op) => boolean - externalDocs (object, optional): Link to external documentation
- security (array, optional): Security schemes
- components (object, optional): Additional component schemas
- info (object, required): API metadata with
- swaggerPath (string, optional): Path for Swagger UI (default:
/docs)
Returns void
Code example
import { app } from 'codehooks-js';
import { z } from 'zod';
const todoSchema = z.object({
title: z.string().min(1).max(200),
completed: z.boolean().default(false)
});
// Enable OpenAPI documentation
app.openapi({
info: {
title: 'Todo API',
version: '1.0.0',
description: 'Task management API'
},
tags: [
{ name: 'Todos', description: 'Todo operations' }
],
filter: (op) => op.method !== 'delete' // Hide DELETE endpoints from docs
}, '/api-docs'); // Swagger UI at /api-docs
app.crudlify({ todos: todoSchema });
export default app.init();
After deployment, visit:
/docs(or custom path) — Interactive Swagger UI with authentication support/openapi.json— Raw OpenAPI 3.0 specification
openapi(spec) middleware
Add OpenAPI metadata to custom routes. Import the openapi middleware helper and use it to document individual endpoints.
Parameters
- spec (object): OpenAPI operation specification
- summary (string): Short description of the endpoint
- description (string, optional): Detailed description
- tags (array): Tags to group the endpoint
- requestBody (object, optional): Request body schema (supports Zod schemas directly)
- parameters (array, optional): Query/path parameters
- responses (object): Response definitions
- security (array, optional): Security requirements (use
[]for public endpoints)
Returns Middleware function
Code example
import { app, openapi } from 'codehooks-js';
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email()
});
app.openapi({
info: { title: 'User API', version: '1.0.0' }
});
// Document a custom POST endpoint with Zod schema
app.post('/users',
openapi({
summary: 'Create a new user',
tags: ['Users'],
requestBody: {
required: true,
content: {
'application/json': {
schema: UserSchema // Zod schema auto-converts to JSON Schema
}
}
},
responses: {
201: { description: 'User created' },
400: { description: 'Validation error' }
}
}),
async (req, res) => {
// Handler logic
res.status(201).json({ id: '123', ...req.body });
}
);
// Document a GET endpoint with query parameters
app.get('/users',
openapi({
summary: 'List users',
tags: ['Users'],
parameters: [
{ name: 'limit', in: 'query', schema: { type: 'integer', default: 20 } },
{ name: 'role', in: 'query', schema: { type: 'string', enum: ['admin', 'user'] } }
],
responses: {
200: { description: 'List of users' }
}
}),
async (req, res) => {
res.json([]);
}
);
export default app.init();
app.createWorkflow(name, desc, workflowJSON, config?)
Create reliable stateful workflows using plain JavaScript.
Parameters
- name (string): Unique identifier for the workflow
- description (string): Human-readable description of the workflow
- steps (WorkflowDefinition): Object containing step definitions
- config (object, optional): Configuration options including
collectionName,queuePrefix,timeout,maxStepCount,workers, and step-specific settings
Returns: Workflow instance for managing the workflow
Code example:
import { app } from 'codehooks-js';
// The workflow definition
const workflow = app.createWorkflow('minimal', 'Minimal workflow example', {
start: (state, goto) => {
goto('end', { ...state, message: 'Hello World' });
},
end: (state, goto) => goto(null, state), // null complete the workflow
});
// A REST API to start a new workflow instance
app.post('/start', async (req, res) =>
res.json(await workflow.start({ prop: 'some value' }))
);
export default app.init();
app.set(key, val)
Set configuration settings for the application. This allows you to configure various aspects of your application behavior.
Parameters
- key: String - the configuration key
- val: any - the configuration value
Returns void
Code example
import { app } from 'codehooks-js';
import handlebars from 'handlebars';
// Set views directory
app.set('views', '/views');
// Set view engine
app.set('view engine', { hbs: handlebars });
// Set other configuration options
app.set('foo', 'bar');
export default app.init();
app.internalFetch(url, options)
Fetch data from another Codehooks API. This method is designed for high speed app-to-app communication within the Codehooks platform, allowing you to make authenticated API calls to other Codehooks applications.
Authentication: Must include x-apikey header for authentication if the target API is not public.
Parameters
- url: String - URL to fetch from (e.g.,
http://myapi-ffee.codehooks.io/dev/api/myroute) - options: Object (optional) - Fetch options including method, headers, body, etc.
Returns Promise<any> - Promise that resolves with the fetched data
Code example
import { app } from 'codehooks-js';
app.get('/fetch-data', async (req, res) => {
try {
// Fetch data from another Codehooks API
const data = await app.internalFetch(
'http://myapi-ffee.codehooks.io/dev/api/users',
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'x-apikey': 'your-target-api-key-here'
}
}
);
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch data' });
}
});
app.post('/sync-user', async (req, res) => {
try {
// POST data to another Codehooks API
const result = await app.internalFetch(
'http://userservice-1234.codehooks.io/dev/api/users',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-apikey': 'your-target-api-key-here'
},
body: JSON.stringify(req.body)
}
);
res.json({ success: true, result });
} catch (error) {
res.status(500).json({ error: 'Failed to sync user' });
}
});
export default app.init();
Read more in the detailed workflow API docs