HTTP decision machine for API gateways and microservices — Node.js port of Webmachine
HTTP is not just if (req.method === 'GET'). A production API has to get dozens of things right: content negotiation,
conditional requests, ETags, caching headers, authorization, correct 4xx/5xx codes for every edge case. Most frameworks
leave all of that to you, and most implementations get it quietly wrong.
ResourceMachine implements the full Webmachine v3 HTTP decision diagram — roughly 40 decision points that correctly model HTTP semantics. You describe your resource; the machine handles the protocol.
ResourceMachine is designed for engineers building API gateways and microservices — backends where HTTP correctness matters and separation of concerns reduces bugs. It is not a web application framework and does not try to be.
If your service needs to:
- Return different representations based on
Acceptheaders - Respect
If-None-Match/If-Modified-Sincefor cache validation - Handle
PUT/DELETE/PATCHwith correct precondition checking - Enforce authorization and permission rules per endpoint
- Do all of the above correctly, every time, without repeating yourself
…ResourceMachine handles the HTTP layer so you handle the business logic.
You define a Resource class per endpoint. Override only the methods relevant to that endpoint — everything else falls back to safe HTTP defaults. The decision machine instantiates your class per request and walks the diagram, calling your methods at the right moment.
import{createServer,Resource}from"resource-machine";classArticleResourceextendsResource{privatearticle: Article|null=null;// Which HTTP methods are allowed?overrideasyncallowedMethods(){return["GET","HEAD","PUT","DELETE"];}// Does this resource exist? Fetch it once, cache on `this`.overrideasyncresourceExists(){this.article=awaitdb.find(this.req.params.id);returnthis.article!==null;}// Serve it — article already fetched above, no second DB call.overrideasynccontentTypesProvided(){return{"application/json": ()=>JSON.stringify(this.article),};}// Accept a PUT body.overrideasynccontentTypesAccepted(){return{"application/json": async()=>{constbody=JSON.parse((awaitthis.req.getBody()).toString());this.article=awaitdb.update(this.req.params.id,body);returntrue;},};}}constserver=createServer({name: "articles-api"});server.addRoute("/articles/:id",ArticleResource);awaitserver.listen(3000);The machine takes care of: 404 when resourceExists() is false, 405 for disallowed methods, 406 when no content
type matches, 412 on failed preconditions, 304 for unmodified resources — all without you touching a status code.
- Node.js 22.0.0 or later
- TypeScript (optional but strongly recommended)
npm install resource-machineFull documentation at jongretar.github.io/ResourceMachine.
Examples are in examples/.
MIT — Copyright © 2026 Jón Grétar Borgþórsson