@ezpost/sdk
v0.2.0
Published
Typed JavaScript/TypeScript client SDK for the EZPost External API
Readme
@ezpost/sdk
A typed TypeScript client SDK for the EZPost External API.
Installation
npm install @ezpost/sdk
# or
pnpm add @ezpost/sdkQuick Start
import { EZPostClient } from '@ezpost/sdk';
const client = new EZPostClient({
apiKey: 'your-api-key-here',
baseUrl: 'https://api.yourdomain.com/api/v1/external', // optional
});
// List published posts
const { posts, count, totalPages } = await client.posts.list({
isPublished: true,
limit: 10,
page: 1,
});
// Get a single post by slug
const { post, relatedPosts } = await client.posts.getBySlug('my-post-slug');
// List categories
const categories = await client.categories.list();Configuration
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| apiKey | string | Yes | — | Your EZPost API key |
| baseUrl | string | No | https://api.yourdomain.com/api/v1/external | API base URL |
| timeout | number | No | 10000 (10s) | Request timeout in milliseconds |
| headers | Record<string, string> | No | {} | Custom headers sent with every request |
API Reference
client.posts.list(query?)
Retrieves a paginated list of posts.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| limit | number | 10 | Posts per page |
| page | number | 1 | Page number |
| isPublished | boolean | — | Filter by published status |
| isFeatured | boolean | — | Filter by featured status |
| categoryId | string | — | Comma-separated category IDs |
| search | string | — | Search in title and description |
| sortBy | 'createdAt' \| 'updatedAt' \| 'totalViews' | 'createdAt' | Sort field |
| sortOrder | 'asc' \| 'desc' | 'desc' | Sort direction |
const response = await client.posts.list({
isPublished: true,
sortBy: 'totalViews',
sortOrder: 'desc',
});
// response.posts, response.count, response.totalPages, response.hasMore, response.hasLessclient.posts.listAll(query?)
Async iterator that automatically paginates through all matching posts.
for await (const post of client.posts.listAll({ isPublished: true })) {
console.log(post.title);
}client.posts.getBySlug(slug)
Retrieves a single published post by its slug, including related posts.
const { post, relatedPosts } = await client.posts.getBySlug('my-post');client.posts.incrementViews(slug)
Increments the view count for a post.
await client.posts.incrementViews('my-post');client.categories.list()
Lists all categories with their subcategories.
const categories = await client.categories.list();
// categories[0].name, categories[0].subcategories[0].nameContent Utilities
Posts support two content formats: Markdown and BlockNote rich text. The isMarkdownFormat flag indicates which format a post uses. These utilities help you handle both formats.
extractPlainText(post)
Returns plain text regardless of content format. For markdown posts, returns the markdown string. For BlockNote posts, joins block content with newlines.
import { extractPlainText } from '@ezpost/sdk';
const post = await client.posts.getBySlug('my-post');
const text = extractPlainText(post.post);parseContent(post)
Returns the parsed content. For markdown posts, returns a string. For BlockNote posts, returns an array of block objects.
import { parseContent } from '@ezpost/sdk';
const post = await client.posts.getBySlug('my-post');
const content = parseContent(post.post);
if (typeof content === 'string') {
// Render as markdown
} else {
// Render as BlockNote blocks
}isMarkdown(post)
Returns true if the post uses markdown format, false for BlockNote.
import { isMarkdown } from '@ezpost/sdk';
if (isMarkdown(post.post)) {
// Use markdown renderer
} else {
// Use BlockNote renderer
}extractTableOfContents(post)
Extracts a table of contents (heading outline) from post content, regardless of format. Returns an array of TocItem objects with id, level, and text.
For markdown posts, parses #-###### heading syntax. For BlockNote posts, filters heading blocks and extracts their text content.
import { extractTableOfContents } from '@ezpost/sdk';
const { post } = await client.posts.getBySlug('my-post');
const toc = extractTableOfContents(post);
// [
// { id: 'introduction', level: 2, text: 'Introduction' },
// { id: 'getting-started', level: 3, text: 'Getting Started' },
// { id: 'conclusion', level: 2, text: 'Conclusion' },
// ]
toc.forEach((item) => {
console.log(`${' '.repeat(item.level - 1)}${item.text} → #${item.id}`);
});Type:
type TocItem = {
readonly id: string; // slug or block id — safe for anchor links
readonly level: number; // 1–6
readonly text: string; // heading text
};Error Handling
All errors are thrown as EZPostError instances with status, code, message, and details properties.
import { EZPostClient, EZPostError } from '@ezpost/sdk';
try {
const post = await client.posts.getBySlug('nonexistent');
} catch (error) {
if (error instanceof EZPostError) {
console.log(error.status); // 404
console.log(error.code); // "Post not found"
console.log(error.message); // "Post not found"
console.log(error.details); // response body
}
}TypeScript
All types are inferred from Zod schemas and exported from the package. You can import them directly:
import type { Post, PostsResponse, Category, ListPostsQuery } from '@ezpost/sdk';ESM Only
This package is ESM-only. It requires Node.js 18+ or a modern bundler that supports ES modules. If you're using CommonJS, use dynamic import:
const { EZPostClient } = await import('@ezpost/sdk');React Hooks
React hooks wrapping TanStack Query are available as a separate sub-export at @ezpost/sdk/react.
Installation
npm install @ezpost/sdk @tanstack/react-query reactProvider Setup
Wrap your app with EZPostClientProvider:
import { EZPostClient, EZPostClientProvider } from '@ezpost/sdk';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const client = new EZPostClient({ apiKey: 'your-api-key' });
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<EZPostClientProvider client={client}>
<YourApp />
</EZPostClientProvider>
</QueryClientProvider>
);
}usePosts
import { usePosts } from '@ezpost/sdk/react';
function PostList() {
const { data, isLoading } = usePosts({ isPublished: true });
if (isLoading) return <p>Loading...</p>;
return (
<ul>
{data.posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}usePost
import { usePost } from '@ezpost/sdk/react';
function PostDetail({ slug }: { slug: string }) {
const { data, isLoading } = usePost(slug);
if (isLoading) return <p>Loading...</p>;
return <h1>{data.post.title}</h1>;
}useCategories
import { useCategories } from '@ezpost/sdk/react';
function CategoryList() {
const { data, isLoading } = useCategories();
if (isLoading) return <p>Loading...</p>;
return (
<ul>
{data.map((cat) => (
<li key={cat.id}>{cat.name}</li>
))}
</ul>
);
}usePostTableOfContents
Memoized hook that extracts a table of contents from a post object. Works with both markdown and BlockNote formats. Returns an empty array on parse errors.
import { usePostTableOfContents } from '@ezpost/sdk/react';
function TableOfContents({ post }: { post: Post }) {
const toc = usePostTableOfContents(post);
if (toc.length === 0) return null;
return (
<nav>
{toc.map((item) => (
<a
key={item.id}
href={`#${item.id}`}
style={{ paddingLeft: `${(item.level - 1) * 12}px` }}
>
{item.text}
</a>
))}
</nav>
);
}License
MIT
