@calmify/next
v1.4.0
Published
Receive articles from Calmify in a Next.js app: a route handler and a one-time pairing command (built on @calmify/node).
Maintainers
Readme
@calmify/next
Receive articles from Calmify in a Next.js (App Router) site. Built on @calmify/node, which also works with Express, React Router and other Node.js servers.
npm install @calmify/next- Create
app/api/calmify/route.ts:import { createCalmifyHandler } from "@calmify/next"; export const runtime = "nodejs"; export const { GET, POST, DELETE } = createCalmifyHandler({ findByKey: async (key) => /* look up a post saved with this key */ null, save: async (post) => { /* save to your DB/CMS, idempotent on post.idempotencyKey */ return { id: 1, url: `https://www.yoursite.com/blog/${post.slug}` }; }, update: async (id, post) => { /* replace post `id` if its slug is post.slug, else return null */ return { id, url: `https://www.yoursite.com/blog/${post.slug}` }; }, unpublish: async (id, slug) => { /* hide post `id` if its slug matches, else return null */ return { id, url: `https://www.yoursite.com/blog/${slug}` }; }, }); npx calmify-paironce to generateCALMIFY_API_KEY; add it to your hosting env and deploy.- In Calmify: Websites → Connection → Connect, then
npx calmify-pair CALM-XXXX-XXXX --endpoint https://www.yoursite.com/api/calmify.
The url you return from save (and findByKey) is the link Calmify shows for the post, so return your real article address (for example /journal/<slug> if that's your route).
Updates. When an article is edited and sent again from Calmify ("Update on site"), the receiver calls update(id, post) with the id your save returned. Replace that post (title, content, meta, cover; post.image is null when the cover was removed), store post.idempotencyKey on it so findByKey finds it on a retry, and return { id, url }. Return null when you have no post with that id and post.slug (Calmify shows "post not found"). Without update the receiver answers 422 updates_not_supported.
Unpublishing. "Unpublish from site" in Calmify sends DELETE with the post's id and slug, and the receiver calls unpublish(id, slug). Hide the post (keep it, so a later update can bring it back) and return { id, url }, or null if there's no such post. Export DELETE from your route too. Without unpublish the receiver answers 422 unpublish_not_supported.
Images inside articles. Add saveImage(image): it gets the checked bytes of each image (image.bytes, image.ext, image.alt, image.slug), stores them and returns the public URL; post.contentHtml then points there. It's called before save/update. Without it, the connection check doesn't list "images" and Calmify sends articles without their inline images.
If a post carries a cover and CALMIFY_ASSET_HOST isn't set on the server yet, the receiver refuses the post (422 asset_host_not_set) instead of saving it without its image; Calmify shows the reason. Re-running npx calmify-pair updates the keys in the env file in place.
Full guide: https://joincalmify.com/docs/connect
