@szymonpiatek/nextwordpress
v0.0.25
Published
Szymon Piątek - Next.js WordPress client (types, queries, fetcher)
Downloads
3,539
Maintainers
Readme
@szymonpiatek/nextwordpress
A strictly typed, high-performance WordPress client for Next.js. This library provides a unified interface for interacting with WordPress via REST API and WPGraphQL, with first-class support for WooCommerce, Yoast SEO, ACF, and more.
Features
- 🚀 Built for Next.js: Optimized for Server Components and SSR.
- 🛡️ Strictly Typed: Full TypeScript support for all API responses.
- 🔗 Dual Integration: Support for both standard REST API and WPGraphQL.
- 🛒 WooCommerce Ready: Deep integration with WooCommerce REST & GQL, including EU Omnibus directive support.
- ⚡ Next.js Optimized: Built-in support for Next.js
fetchcache, revalidation, and tags. - 🔍 Preview Mode: Built-in FaustJS-compatible draft/preview handlers for Next.js.
- 📝 Contact Form 7: Ready-to-use CF7 REST API integration.
- 👍 WP ULike: Like/unlike posts, comments, and custom item types via REST API.
- 🍪 Cookie Consent: GDPR-compliant cookie consent handler compatible with Cookie Notice & Compliance plugin.
- 📦 Tree-shakeable: Only bundle what you use.
Installation
npm install @szymonpiatek/nextwordpressQuick Start
1. WordPress REST API
The REST client is ideal for standard WordPress data fetching.
import { createWordPressClient } from '@szymonpiatek/nextwordpress';
const wp = createWordPressClient({
serverURL: 'https://api.yourstore.com', // Internal URL (e.g., Docker)
clientURL: 'https://yourstore.com', // Public URL
});
// Fetch posts
const posts = await wp.getPosts({ _embed: true });
// Fetch a single page by slug
const page = await wp.getPageBySlug('about-us');2. WPGraphQL
Use the GraphQL client for complex queries and better performance.
import { createWPGraphQLClient } from '@szymonpiatek/nextwordpress';
const gq = createWPGraphQLClient({
serverURL: 'https://yourstore.com/graphql',
});
// Fetch posts via GraphQL
const { nodes: posts } = await gq.getPosts({ first: 10 });Integrations
WooCommerce
Full support for products, orders, customers, and more. Includes a dedicated helper for the EU Omnibus Directive.
import { createWooCommerceClient, withOmnibus } from '@szymonpiatek/nextwordpress';
const woo = createWooCommerceClient({
serverURL: 'https://yourstore.com',
consumerKey: process.env.WC_CONSUMER_KEY!,
consumerSecret: process.env.WC_CONSUMER_SECRET!,
});
const product = await woo.getProductById(123);
// Apply Omnibus directive helpers (lowest price in 30 days)
const productWithHistory = withOmnibus(product);
console.log(productWithHistory.omnibus.lowestPrice);FaustJS / Preview Mode
Enable WordPress draft preview in Next.js using FaustJS-compatible route handlers. Requires the FaustJS WordPress plugin and a FAUST_SECRET_KEY environment variable.
// app/api/faust/[...route]/route.ts
import { createFaustAuthHandler } from '@szymonpiatek/nextwordpress';
import { NextRequest } from 'next/server';
const handler = createFaustAuthHandler({
wpUrl: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
secretKey: process.env.FAUST_SECRET_KEY!,
});
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ route: string[] }> }
) {
const { route } = await params;
return handler(request, route.join('/'));
}// app/api/preview/route.ts
import { createPreviewHandler } from '@szymonpiatek/nextwordpress';
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';
export const GET = createPreviewHandler(
{
wpUrl: process.env.WORDPRESS_URL!,
secretKey: process.env.FAUST_SECRET_KEY!,
previewPath: 'posts', // default
},
{
enableDraftMode: async () => { (await draftMode()).enable(); },
redirect,
},
);WP ULike
Add like/unlike functionality to posts, comments, and any custom item type. Requires the WP ULike WordPress plugin.
import { createWPULikeClient } from '@szymonpiatek/nextwordpress';
const ulike = createWPULikeClient({
serverURL: process.env.WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
// Get like stats for a post
const stats = await ulike.getLikeStats({ id: 42, type: 'post' });
console.log(stats.count); // 17
// Check if the current user already liked (requires auth)
const check = await ulike.checkLikeStatus({ id: 42 });
console.log(check.liked); // false
// Like a post (requires auth token or nonce)
const result = await ulike.vote({ id: 42, status: 'like' }, authToken);
console.log(result.count); // 18React hook for client-side use:
'use client';
import { useWPULike } from '@szymonpiatek/nextwordpress/hooks';
function LikeButton({ postId }: { postId: number }) {
const { data, vote } = useWPULike(config, { id: postId });
return (
<button onClick={() => vote('like')}>
👍 {data?.count ?? 0}
</button>
);
}Cookie Consent
GDPR-compliant cookie consent management compatible with the Cookie Notice & Compliance WordPress plugin. Uses the same cookie_notice_accepted cookie so consent set by the plugin is respected in Next.js, and vice versa.
1. Create the API route:
// app/api/cookie-consent/route.ts
import { createCookieConsentHandler } from '@szymonpiatek/nextwordpress';
const handler = createCookieConsentHandler();
export const GET = handler;
export const POST = handler;
export const DELETE = handler;2. Read consent in Server Components (e.g. to conditionally load analytics scripts):
import { parseCookieConsent } from '@szymonpiatek/nextwordpress';
import { cookies } from 'next/headers';
export default async function RootLayout({ children }) {
const cookieStore = await cookies();
const req = new Request('http://localhost', {
headers: { cookie: cookieStore.toString() },
});
const consent = parseCookieConsent(req);
return (
<html>
<body>
{consent?.analytics && <GoogleAnalyticsScript />}
{children}
</body>
</html>
);
}3. React hook for your CookiesConsent component:
'use client';
import { useCookieConsent } from '@szymonpiatek/nextwordpress/hooks';
function CookiesConsent() {
const { consent, isLoaded, setConsent, clearConsent } = useCookieConsent();
if (!isLoaded || consent !== null) return null;
return (
<div>
<button onClick={() => setConsent({ analytics: true, marketing: true })}>
Akceptuj wszystkie
</button>
<button onClick={() => setConsent({ analytics: false, marketing: false })}>
Odrzuć opcjonalne
</button>
</div>
);
}| Option | Default | Description |
|--------|---------|-------------|
| cookieName | cookie_notice_accepted | Cookie name (matches Cookie Notice plugin) |
| maxAge | 31536000 (1 year) | Cookie lifetime in seconds |
Password Reset
Two-step password reset flow for WordPress and WooCommerce customers (WooCommerce customers are standard WordPress users). Uses WPGraphQL mutations — requires the WPGraphQL plugin.
Flow:
- User submits email → WordPress sends a reset link via email
- User clicks the link → your Next.js page receives
keyandloginas query params - User submits a new password → password is changed
Headless setup: By default WordPress emails a link to
wp-login.php. To redirect it to your Next.js page, add a filter in your theme or plugin:add_filter('retrieve_password_message', function ($message, $key, $login) { $reset_url = "https://your-nextjs-app.com/reset-password?key={$key}&login={$login}"; return str_replace(network_site_url("wp-login.php?action=rp&key={$key}&login={$login}"), $reset_url, $message); }, 10, 3);
Step 1 — request a reset email:
// Server-side
import { createWPGraphQLMutationsClient } from '@szymonpiatek/nextwordpress';
const gql = createWPGraphQLMutationsClient({
serverURL: process.env.WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
await gql.sendPasswordResetEmail({ username: '[email protected]' });// Client-side hook
'use client';
import { useSendPasswordResetEmail } from '@szymonpiatek/nextwordpress/hooks';
function ForgotPasswordForm() {
const { trigger, isMutating, data, error } = useSendPasswordResetEmail({
serverURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
return (
<form onSubmit={async (e) => {
e.preventDefault();
const email = new FormData(e.currentTarget).get('email') as string;
await trigger({ username: email });
}}>
<input name="email" type="email" required />
<button type="submit" disabled={isMutating}>Send reset link</button>
{data && <p>Check your inbox!</p>}
{error && <p>Something went wrong. Try again.</p>}
</form>
);
}Step 2 — set a new password (on the page that receives the key and login URL params):
// Server-side
await gql.resetUserPassword({
key: searchParams.key,
login: searchParams.login,
password: 'NewSecurePassword123!',
});// Client-side hook
'use client';
import { useResetUserPassword } from '@szymonpiatek/nextwordpress/hooks';
function ResetPasswordForm({ resetKey, login }: { resetKey: string; login: string }) {
const { trigger, isMutating, data, error } = useResetUserPassword({
serverURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
return (
<form onSubmit={async (e) => {
e.preventDefault();
const password = new FormData(e.currentTarget).get('password') as string;
await trigger({ key: resetKey, login, password });
}}>
<input name="password" type="password" minLength={8} required />
<button type="submit" disabled={isMutating}>Set new password</button>
{data?.user && <p>Password changed! You can now log in.</p>}
{error && <p>Link is invalid or expired. Request a new one.</p>}
</form>
);
}Wishlist (YITH & TI)
Support for popular WooCommerce wishlist plugins. Both integrations require a JWT Bearer token for user-scoped operations.
import {
createYithWishlistFetcher,
createWishlistQueries,
createTIWishlistFetcher,
createTIWishlistQueries
} from '@szymonpiatek/nextwordpress';
// YITH Wishlist
const yithFetcher = createYithWishlistFetcher({ serverURL, token });
const yith = createWishlistQueries(yithFetcher);
const myWishlist = await yith.getWishlists();
// TI Wishlist
const tiFetcher = createTIWishlistFetcher({ serverURL, token });
const ti = createTIWishlistQueries(tiFetcher);
const tiWishlist = await ti.getWishlistsByUser(userId);Contact Form 7
Submit Contact Form 7 forms directly from Next.js via the CF7 REST API. Requires the Contact Form 7 WordPress plugin.
import { createCF7Queries } from '@szymonpiatek/nextwordpress';
const cf7 = createCF7Queries({
serverURL: process.env.WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
const result = await cf7.submitForm(123, {
your_name: 'Jan Kowalski',
your_email: '[email protected]',
your_message: 'Hello!',
});
if (result.status === 'mail_sent') {
console.log('Message sent successfully');
} else if (result.status === 'validation_failed') {
console.log('Validation errors:', result.invalid_fields);
}For client components use the useCF7Submit hook:
'use client'
import { useCF7Submit } from '@szymonpiatek/nextwordpress/client';
function ContactForm() {
const { trigger, isMutating, data } = useCF7Submit({
serverURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
clientURL: process.env.NEXT_PUBLIC_WORDPRESS_URL!,
});
return (
<form onSubmit={async (e) => {
e.preventDefault();
await trigger({ formId: 123, fields: { 'your-name': 'Jan', 'your-email': '[email protected]' } });
}}>
<button type="submit" disabled={isMutating}>Send</button>
{data?.status === 'mail_sent' && <p>Sent!</p>}
</form>
);
}Yoast SEO & ACF
Easily fetch SEO metadata and ACF fields via WPGraphQL.
import {
createWPGraphQLClient,
buildPostWithACFQuery
} from '@szymonpiatek/nextwordpress';
const gq = createWPGraphQLClient({ serverURL: '...' });
// Fetch post with Yoast SEO data
const post = await gq.getPostBySlug('hello-world');
// post.seo.title, post.seo.metaDesc etc.
// ACF support
const acfQuery = buildPostWithACFQuery('my_field_group');Caching & Revalidation
The library leverages the Next.js Extended fetch API for server-side ISR caching and SWR for client-side polling.
Server-side (ISR)
Pass cacheTTL in the config to control how long Next.js caches responses before revalidating. Defaults to 300 seconds (5 minutes).
const wp = createWordPressClient({
serverURL: 'http://wordpress:8080',
clientURL: 'https://example.com',
cacheTTL: 60, // revalidate every 60 seconds
});
const gq = createWPGraphQLClient({
serverURL: 'https://example.com',
cacheTTL: 60,
});Mutations (wpMutate, gqlMutate) always bypass the cache with cache: 'no-store' regardless of cacheTTL.
All queries accept optional FetchOptions for fine-grained control over caching and headers:
const posts = await wp.getPosts({}, {
next: {
tags: ['my-posts-tag'],
revalidate: 3600
},
headers: { 'Authorization': 'Bearer ...' }
});
// Invalidate from a webhook route:
// revalidateTag('my-posts-tag');Client-side (SWR)
React hooks accept swrOptions as the last argument. Pass refreshInterval (in ms) to poll for updates in the browser:
import { usePosts, useGQLPosts } from '@szymonpiatek/nextwordpress';
// Poll every 30 seconds
usePosts(config, filter, { refreshInterval: 30_000 });
// GraphQL equivalent
useGQLPosts(config, 10, undefined, filter, { refreshInterval: 30_000 });Advanced Configuration
Next.js 13/14+ Internal/External URLs
The library handles the common pattern where your Next.js server communicates with WordPress over a different URL (e.g., inside a Docker network) than the client's browser.
const wp = createWordPressClient({
serverURL: 'http://wordpress-container:80', // Used for SSR
clientURL: 'https://public-domain.com', // Used for client-side links/assets
});API Modules
| Module | Description |
| --- | --- |
| core | Posts, Pages, Categories, Tags, Media, Menus, Comments, Authors, Site Settings, Search, Revisions, Post Types, Taxonomies, Templates, Template Parts, Navigation, Block Patterns, WP Settings, Themes, Plugins. |
| woocommerce | Full WooCommerce REST API v3: Products, Categories, Tags, Attributes, Reviews, Shipping Classes, Orders, Order Notes, Order Refunds, Coupons, Customers, Payment Gateways, Taxes, Shipping Zones/Methods, Webhooks, Settings, Reports, Data, System Status, Wishlists (YITH & TI). |
| graphQl | Core GQL queries, WooCommerce GQL, ACF, Yoast SEO, CPT, Password Reset. |
| contactForm7 | Contact Form 7 REST API submission. |
| wpulike | WP ULike REST API: like stats, like/unlike voting, status check. |
| nextjs/faust | FaustJS-compatible auth and preview/draft mode handlers. |
| nextjs/cookieConsent | GDPR cookie consent handler compatible with Cookie Notice & Compliance plugin. |
| shared | Common types, URL helpers, and schemas. |
Coverage
✅ supported · 🔐 requires auth token · N/A not supported by the API · — not implemented
WordPress Core
| Module | REST read | REST create | REST update | REST delete | GQL read | GQL create | GQL update | GQL delete | |--------|:---------:|:-----------:|:-----------:|:-----------:|:--------:|:----------:|:----------:|:----------:| | posts | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | pages | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | categories | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | tags | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | comments | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | | authors | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | | menus | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | N/A | N/A | N/A | | media | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | site settings | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | | search | ✅ | N/A | N/A | N/A | ✅ | N/A | N/A | N/A | | revisions | ✅ 🔐 | N/A | N/A | ✅ 🔐 | ✅ 🔐 | N/A | N/A | N/A | | post types | ✅ | N/A | N/A | N/A | ✅ | N/A | N/A | N/A | | taxonomies | ✅ | N/A | N/A | N/A | ✅ | N/A | N/A | N/A | | templates | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | N/A | N/A | N/A | | template parts | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | N/A | N/A | N/A | | navigation | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | N/A | N/A | N/A | | block patterns | ✅ | N/A | N/A | N/A | ✅ | N/A | N/A | N/A | | wp settings | ✅ 🔐 | N/A | ✅ 🔐 | N/A | ✅ | N/A | N/A | N/A | | themes | ✅ 🔐 | N/A | N/A | N/A | ✅ | N/A | N/A | N/A | | plugins | ✅ 🔐 | N/A | ✅ 🔐 | ✅ 🔐 | ✅ | N/A | N/A | N/A |
Notes:
- Comments
createdoes not require auth when WordPress allows anonymous posting. - Authors GQL
createusesregisterUser(open registration) — no auth needed by default. - Menus GQL mutations are N/A: WPGraphQL does not natively support menu/menu-item mutations.
- Media REST
createuploads files as binary (Content-Dispositionattachment); metadata can be set in the same call. - Media GQL
createrequires a publicly accessiblefilePathURL (WPGraphQL fetches the file server-side).
WPGraphQL Extensions
| Module | REST | GQL read | Notes | |--------|:----:|:--------:|-------| | Yoast SEO | — | ✅ | embedded in post/page queries | | ACF | — | ✅ | custom fragment builder | | Custom Post Types | — | ✅ | generic CPT query helper |
WooCommerce
| Module | REST read | REST create | REST update | REST delete | GQL read | |--------|:---------:|:-----------:|:-----------:|:-----------:|:--------:| | products | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | | categories | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | | tags | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | | orders | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ | | coupons | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ | | customers | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | ✅ | | omnibus (EU) | ✅ | — | — | — | N/A | | payment gateways | ✅ 🔐 | — | — | — | N/A | | order notes | ✅ 🔐 | ✅ 🔐 | — | ✅ 🔐 | N/A | | order refunds | ✅ 🔐 | ✅ 🔐 | — | ✅ 🔐 | N/A | | product attributes | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | attribute terms | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | product reviews | ✅ | ✅ | ✅ 🔐 | ✅ 🔐 | N/A | | shipping classes | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | tax rates | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | tax classes | ✅ 🔐 | ✅ 🔐 | — | ✅ 🔐 | N/A | | shipping zones | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | shipping zone locations | ✅ 🔐 | — | ✅ 🔐 | — | N/A | | shipping zone methods | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | shipping methods | ✅ 🔐 | — | — | — | N/A | | webhooks | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | N/A | | settings | ✅ 🔐 | — | ✅ 🔐 | — | N/A | | reports | ✅ 🔐 | — | — | — | N/A | | data (currencies/countries) | ✅ | — | — | — | N/A | | system status | ✅ 🔐 | — | — | — | N/A | | TI Wishlist | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | — | | YITH Wishlist | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | ✅ 🔐 | — |
Notes:
- WooCommerce REST mutations use consumer key/secret authentication (not Bearer token).
- Orders
createis public (no auth required for guest checkout); update/delete require admin credentials. - Customers
createis public (self-registration); update/delete require admin credentials. - Product reviews
createis public; update/delete require admin credentials. - Data endpoints (currencies, countries, continents, time zones) are public read-only.
- Omnibus is a REST utility helper — no equivalent in WPGraphQL WooCommerce.
- GQL mutations for WooCommerce are not supported by WPGraphQL WooCommerce core.
Next.js Utilities
| Utility | Support | Notes |
|--------------------------|:-------:|-------------------------------------------------------------------|
| ISR revalidation handler | ✅ | createRevalidationHandler — webhook-triggered cache tag revalidation |
| FaustJS auth handler | ✅ | createFaustAuthHandler — token exchange + httpOnly cookie management |
| Preview / draft mode | ✅ | createPreviewHandler — Next.js draftMode() integration |
| Contact Form 7 | ✅ | createCF7Queries — CF7 REST API form submission |
| WP ULike | ✅ | createWPULikeClient — like stats, vote, status check; hooks: useWPULike, useWPULikeStats, useWPULikeCheck |
| Cookie Consent | ✅ | createCookieConsentHandler — GDPR consent cookie (get/set/clear); parseCookieConsent — server-side read; hook: useCookieConsent |
Development
# Install dependencies
npm install
# Run tests
npm test
# Run integration tests (requires Docker)
npm run test:integration
# Build package
npm run buildLicense
This project is currently UNLICENSED. (C) Szymon Piątek.
