A drop‑in HTTP gateway that turns every .ts or .js file in a functions/
folder into a POST endpoint — with Keycloak JWT verification, TypeScript
support via @swc‑node/register, and zero vendor lock‑in.
Why another “serverless” runner?
You already have a database (PostgreSQL) and a job queue (Graphile Worker). All you need is a thin layer to host custom business logic without dragging in AWS Lambda, Vercel, or new pricing models.
Ship this image, mount your code, done.
- Just mount & run – no repo cloning, no build step for handlers
- TypeScript out‑of‑the‑box – SWC JIT‑compiles only what’s needed
- Keycloak / OIDC auth – RS256 / ES256 via JWKS, decoded claims injected into every handler
- Hot‑pluggable – drop a file → restart container → new route appears
- Extendable image – build your own child image to bundle function dependencies or CI artifacts
docker run -p 9010:9010 \
-e JWKS_URL=https://keycloak.example.com/realms/demo/protocol/openid-connect/certs \
-e JWT_ISSUER=https://keycloak.example.com/realms/demo \
-v $(pwd)/functions:/functions \
ghcr.io/appritechnologies/functions-runtime:latest-
Create a
functions/hello.tsfile:export default async ({ req, user }) => { const { name = "world" } = req.body ?? {}; return { msg: `Hello, ${name}!`, user }; };
-
Call it:
curl -X POST http://localhost:9010/functions/hello \ -H "Authorization: Bearer <keycloak‑token>" \ -H "Content-Type: application/json" \ -d '{"name":"Yasiru"}'
type Handler = (ctx: {
req: FastifyRequest;
reply: FastifyReply;
user: JWTPayload; // decoded claims from Keycloak
}) => unknown | Promise<unknown>;- Route =
/functions/<relative/path/to/file>(extension stripped) - Only POST is exposed by default (add GET/PUT in your copy if you wish).
-
Lightweight (bind‑mount):
functions/ ├─ package.json # add axios, uuid, etc. └─ *.tsThen inside the container:
npm --prefix /functions ci --omit=dev
-
Production‑grade (child image):
FROM ghcr.io/appritechnologies/functions-runtime:latest WORKDIR /functions COPY functions/package.json . RUN npm ci --omit=dev COPY functions . WORKDIR /app # back to runtime dir
docker build -t my‑org/my‑functions:prod -f Dockerfile.app .
| Variable | Description |
|---|---|
PORT |
Port to expose (default 9010) |
FUNCTIONS_DIR |
Folder to scan for handlers (default /functions) |
JWKS_URL |
Keycloak JWKS endpoint |
JWT_ISSUER |
Expected iss claim (realm URL) |
# Hot‑reload gateway and handlers
npm run dev- Uses nodemon & @swc‑node/register – instant reloads, no compile step.
services:
functions:
image: ghcr.io/appritechnologies/functions-runtime:latest
environment:
JWKS_URL: https://keycloak.example.com/realms/demo/protocol/openid-connect/certs
JWT_ISSUER: https://keycloak.example.com/realms/demo
volumes:
- ./functions:/functions:ro
networks: [internal]
restart: unless-stopped