The Node.js CMS Built for JavaScript Teams

ApostropheCMS is built on Node.js and Express from the ground up. Your frontend and CMS share the same language, tooling, and development workflow, so JavaScript teams can build and maintain the entire application without context switching.

Why the runtime choice matters for your team

The CMS market has long been dominated by PHP: WordPress alone powers over 40% of the web. But today, much of the frontend work requires JavaScript, and new web developers are trained from the start on JavaScript frameworks. So what was a reasonable historical decision now creates a real organizational cost for JavaScript teams: a separate mental model, toolchain, and hiring requirement for backend expertise that lives outside your core competency.

ApostropheCMS eliminates that split. Your frontend developers can read and contribute to the CMS layer without a context switch. One CI/CD pipeline, one package manager, one set of linting and testing tools. And when you need to hire, you're drawing from the largest developer talent pool in the world rather than one increasingly associated with legacy maintenance.

Common questions about building on Node.js

Can I use any npm package inside ApostropheCMS?

ApostropheCMS modules are Node.js modules. That means any npm package you'd use in a Node.js application (SDKs, API clients, utility libraries) works the same way here. There's no CMS-specific plugin system to route through, no adapter layer to build.

Pulling in Shopify products means reaching for @shopify/shopify-api the same way you would anywhere. Integrating Salesforce means importing the SDK directly into an ApostropheCMS module and getting to work. Want to share that integration logic across the whole site? Wrap it in an async component. And when you're hitting an external API repeatedly, the built-in caching module prevents you from hammering it on every request. If it works in Node.js, it works in ApostropheCMS.

A standard npm package, imported directly into an Apostrophe module, ready for further integration.

javascript
// modules/shopify-product/index.js import Shopify from 'shopify-api-node'; export default { async init(self) { self.shopify = new Shopify({ shopName: process.env.SHOPIFY_SHOP_NAME, apiKey: process.env.SHOPIFY_API_KEY, password: process.env.SHOPIFY_PASSWORD }); }, components(self) { return { showProduct(req, data) { // Your Shopify API call logic here const product = await self.shopify.get('...'); return { title: product.title, features: product.features }; } }; } };

How does ApostropheCMS handle custom API routes?

ApostropheCMS is built on Express and adds a cleaner layer for writing custom API routes. Async functions work as expected. Exceptions are caught automatically without crashing the application. You can return values directly without invoking res.send() every time. Less boilerplate, and fewer bugs that sneak in when you're wiring up Express routes by hand.

The hard way: raw Express

javascript
export default { init() { app.get('/api/v1/product/cheapest', async (req, res) => { try { const cheapest = await self.getCheapest(req); res.send({ cheapest }); } catch (err) { res.status(500).send({ error: err.message }); } }); } }

The easy way: ApostropheCMS API routes

javascript
export default { apiRoutes(self) { return { get: { // Responds automatically as /api/v1/product/cheapest async cheapest(req) { const cheapest = await self.getCheapest(req); return { cheapest }; // Return value sent automatically // Exceptions caught automatically } } }; } };

How does the ApostropheCMS module system work?

Every feature in an ApostropheCMS project, including content types, widgets, pages, and more, is implemented as an ApostropheCMS module. Modules are plain Node.js files that export a configuration object, organized into clearly defined sections for schema fields, server-side methods, event handlers, Express middleware, and other clearly labeled, well-defined functionality. This helps developers understand exactly where to put their code. The framework itself is built from modules using the same patterns your project code uses, so nothing is hidden or special-cased.

The system is designed for extension rather than modification. Any module can inherit from another, picking up all of its fields and behavior and adding to them. When you need to customize inherited behavior, you can wrap the original function rather than replace it entirely. Third-party extensions use the same mechanism, enhancing existing modules without forking them. The result is that customization at any level, from tweaking a built-in content type to replacing core functionality, follows a single consistent pattern.

Does ApostropheCMS fit into a standard Node.js workflow?

Because an ApostropheCMS project is a Node.js application, it fits into the same development workflow as any other Node.js app. The same package manager, same CI/CD patterns, same environment configuration. You're not maintaining two separate toolchains for two separate systems.

How does my CMS choice impact hiring and retention?

JavaScript is the most widely used programming language among professional developers. Building on a Node.js CMS means your hiring pool is significantly larger than it would be for a PHP-based platform. It also means the developers you hire are more likely to want to work on the CMS layer — which matters more than it sounds when content infrastructure tends to be the last thing anyone volunteers to own.

Alt Studios, a South African agency delivering enterprise projects for customers including Toyota, Lexus, and Coca-Cola, explicitly cites ApostropheCMS's Node.js architecture as a factor in attracting and retaining top development talent.

Hear it straight from Alt Studios

When we can rely on the reusability of the component libraries, we have time to actually improve our craft and improve the quality of the work that we do. That just speaks to the power of ApostropheCMS.

Nick Bester

Head of Technology and Development, Alt Studios

How much control do I have over what editors can do?

Complete control over the schema, and strong protection at the content level. You define the fields, the allowed widget types, and which areas of the page editors can touch — editors can only work within the structure you've built.

At the content level, ApostropheCMS uses our widely adopted open-source sanitize-html Node.js library to filter what goes into rich text fields automatically based on the HTML elements and classes you decide to configure. When an editor pastes content from an external source — a Word doc, a Google Doc, a random webpage — it's filtered by our rich text editor before it ever hits the DOM, and again by sanitize-html before it is saved to the database. Nothing an editor pastes can introduce unexpected markup or override your styles. Your responsive CSS is safe with us.

At the same time, ApostropheCMS allows you to safely grant flexibility to editors by enabling features like our layout widget and global and widget-level style editing.

How much backend code do you actually need to write when building with ApostropheCMS?

Knowing JavaScript doesn't automatically mean you’re ready to own a Node.js backend. Fortunately, ApostropheCMS automatically gives you all the logic necessary to store your content types just by creating a schema of fields. In many cases, little or no custom Node.js code is needed. So ApostropheCMS significantly lowers the barrier to entry on the back end.

Of course, it doesn’t eliminate it completely in every case. For instance, if you want to create a custom integration with a third-party API, then you’ll need to write some Node.js code to invoke that API. ApostropheCMS simplifies that work by making it easy to wrap that API call as an async component.

Single sign-on

Want your editors to log in via their Google accounts or any other passportjs-compatible identity provider? Try our Passport Bridge module.

Want to let the public sign up for accounts? Try our Signup: Account Creation module, which implements the traditional email-based confirmation flow and allows the developer to decide what users can do once they have access.

Need to help your new users get work done in a third-party system? Combine both modules to take advantage of robust support for secondary accounts and API calls in ApostropheCMS.