Platformatic Node
The Platformatic Node allows to run a Fastify, Express, Koa or plain Node application as a Platformatic Runtime application with no modifications.
Getting Started
Create or copy your application inside the applications, services or web folder. If you are not using autoload, you also have to explictly add the new application.
You are all set, you can now start your runtime as usual via wattpm dev or wattpm start.
Install
npm install @platformatic/node
Example configuration file
Create a watt.json in the root folder of your application with the following contents:
{
"$schema": "https://schemas.platformatic.dev/@platformatic/node/2.0.0.json",
"application": {
"basePath": "/frontend"
}
}
Specify application info
Some info can be specified for Node.js applications through the @platformatic/globals Runtime APIs. Currently, a few lines of code must be added.
OpenAPI and GraphQL schema
It's possible for the node applications to expose the OpenAPI or GraphQL schemas, if any. This can be done adding few lines of code, e.g. for fastify:
import { getLogger, setOpenapiSchema } from '@platformatic/globals'
import fastify from 'fastify'
import fastifySwagger from '@fastify/swagger'
export async function build () {
const server = fastify({
loggerInstance: getLogger()
})
await server.register(fastifySwagger, {
openapi: {
openapi: '3.0.0',
info: {
title: 'Test Fastify API',
description: 'Testing the Fastify swagger API',
version: '0.1.0'
},
}
})
server.addHook('onReady', async () => {
const schema = server.swagger()
setOpenapiSchema(schema)
})
Connection String
It's possible to specify if a node application uses a connection string (and which one). This is useful to map which application uses which database and to potentialy track database changes.
import { setConnectionString } from '@platformatic/globals'
import { createServer } from 'node:http'
setConnectionString('postgres://dbuser:dbpass@mydbhost/apidb')
const server = createServer((_req, res) => {
res.end(JSON.stringify({ ok: true }))
})
server.listen(0)
Architecture
If your server entrypoint exports a create function, then Platformatic Node will execute it and then will wait for it to return a server object. In this situation the server will be used without starting a TCP server. The TCP server is started if the application is the runtime entrypoint.
If your server entrypoint does not export a function, then Platformatic runtime will execute the function and wait for a TCP server to be started.
In both cases, the listening port is always modified and chosen randomly, overriding any user or application setting.
If the application uses the commands property then it's always responsible to start a HTTP server and the create functions are not supported anymore.
In all cases, Platformatic runtime will modify the server port replacing it with a random port and then it will integrate the external application in the runtime.
If your application entrypoint exports a create or build function that returns an object with isBackgroundApplication set to true, then Platformatic Node will treat the application as a background application which doesn't expose any HTTP port. If the returned object has a close function, it will be called upon application shutdown as close(app), where app is the returned object.
Alternatively, your application entrypoint can export a hasServer variable set to false, or you can set the node.hasServer property to false in your watt.json file. To gracefully shut down an application with hasServer=false, you may export a close function that will be called upon application shutdown.
HTTPS
When a @platformatic/node application is the Watt entrypoint, configure HTTPS in the runtime server.https object:
{
"$schema": "https://schemas.platformatic.dev/wattpm/3.0.0.json",
"entrypoint": "api",
"server": {
"hostname": "127.0.0.1",
"port": 3042,
"https": {
"key": { "path": "./certs/server.key" },
"cert": { "path": "./certs/server.crt" }
}
},
"applications": [
{
"id": "api",
"path": "./services/api"
}
]
}
Use getAdditionalServerOptions() in your Node.js application and pass the returned options to node:https.createServer():
import { getAdditionalServerOptions } from '@platformatic/globals'
import { createServer } from 'node:https'
const server = createServer(getAdditionalServerOptions(), (req, res) => {
res.end('ok')
})
server.listen(0)
Watt reads key and cert file paths before loading the application, so getAdditionalServerOptions() returns the sanitized TLS options that node:https expects. reuseTcpPorts remains enabled by default and HTTPS servers use SO_REUSEPORT when the current Node.js version and operating system support it.
Example applications entrypoints
Fastify with build function
import { getBasePath, getLogLevel } from '@platformatic/globals'
import fastify from 'fastify'
export function create () {
const app = fastify({
logger: { level: getLogLevel({ throwOnMissing: false }) ?? 'info' }
})
const prefix = getBasePath({ throwOnMissing: false }) ?? ''
app.get(`${prefix}/env`, async () => {
return { production: process.env.NODE_ENV === 'production' }
})
return app
}
Express with no build function
import { getBasePath } from '@platformatic/globals'
import express from 'express'
const app = express()
const prefix = getBasePath({ throwOnMissing: false }) ?? ''
app.get(`${prefix}/env`, (req, res) => {
res.send({ production: process.env.NODE_ENV === 'production' })
})
app.listen(3000)
Background only application
export async function create () {
const id = setInterval(() => console.log('alive'), 10_000)
return {
isBackgroundApplication: true,
id,
async close (app) {
clearInterval(app.id)
}
}
}
Alternatively, for modules that start background work when imported, export hasServer as false:
import { getMessaging } from '@platformatic/globals'
export const hasServer = false
const messaging = getMessaging()
messaging.handle('ping', () => 'pong')
const timeoutId = setTimeout(() => console.log('done'), 10_000)
// Optionally provide a close function
export async function close () {
clearTimeout(timeoutId)
}