Skip to main content

News Articles API

Overview​

The News Articles API provides comprehensive access to Ring Platform's news and content management system. This endpoint supports advanced filtering, pagination, search capabilities, and content creation for administrators.

Endpoint Details​

  • URL: /api/news
  • Methods: GET, POST
  • Authentication: GET (Optional), POST (Required - Admin only)
  • Rate Limit: 120 requests per minute for GET, 30 for POST

Ring Platform News System​

Content Strategy​

Ring Platform's news system is designed for professional networking and industry insights:

Content Categories​

  • Product Updates: Platform feature announcements
  • Industry News: Technology and business insights
  • Company Spotlights: Entity and member highlights
  • Event Coverage: Conference and meetup reports
  • Technical Articles: Development and engineering content

Visibility & Access Control​

  • Public: Open access articles for general audience
  • Member: Exclusive content for verified members
  • Confidential: Premium insights for confidential-tier users

Content Management Features​

  • SEO Optimization: Meta titles, descriptions, keywords
  • Rich Media: Featured images and gallery support
  • Social Integration: Sharing and engagement features
  • Analytics: View counts and engagement metrics

GET - List News Articles​

Query Parameters​

ParameterTypeDescriptionDefault
categorystringFilter by news category-
statusstringFilter by status (draft, published, archived)published
visibilitystringFilter by visibility (public, member, confidential)-
featuredbooleanFilter featured articles only-
authorIdstringFilter by author ID-
searchstringSearch in title, excerpt, content, tags-
tagsstringComma-separated list of tags-
limitnumberNumber of articles per page10
offsetnumberNumber of articles to skip0
sortBystringSort field (publishedAt, title, views)publishedAt
sortOrderstringSort direction (asc, desc)desc

Request Format​

GET /api/news?category=product-updates&limit=20&featured=true

Response Format​

Success Response (200 OK)​

{
"success": true,
"data": [
{
"id": "news_123456789",
"title": "Ring Platform Launches Advanced Entity Management",
"slug": "ring-platform-launches-advanced-entity-management",
"excerpt": "Discover the new features that make managing your organization profile easier than ever",
"content": "We're excited to announce the launch of our enhanced entity management system...",
"authorId": "user_987654321",
"authorName": "Ring Platform Team",
"category": "product-updates",
"tags": ["platform", "entities", "features", "management"],
"featuredImage": "https://storage.ring.ck.ua/news/entity-management-hero.jpg",
"gallery": [
"https://storage.ring.ck.ua/news/gallery/screenshot1.jpg",
"https://storage.ring.ck.ua/news/gallery/screenshot2.jpg"
],
"status": "published",
"visibility": "public",
"featured": true,
"views": 1247,
"likes": 89,
"comments": 23,
"publishedAt": "2025-01-14T10:00:00Z",
"createdAt": "2025-01-14T09:30:00Z",
"updatedAt": "2025-01-14T17:20:00Z",
"seo": {
"metaTitle": "Ring Platform Launches Advanced Entity Management",
"metaDescription": "Discover the new features that make managing your organization profile easier than ever",
"keywords": ["ring platform", "entity management", "features"]
}
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 1
},
"filters": {
"category": "product-updates",
"status": "published",
"featured": true,
"limit": 20,
"offset": 0,
"sortBy": "publishedAt",
"sortOrder": "desc"
}
}

Code Examples​

JavaScript/TypeScript​

interface NewsArticle {
id: string;
title: string;
slug: string;
excerpt: string;
content: string;
authorId: string;
authorName: string;
category: string;
tags: string[];
featuredImage?: string;
gallery: string[];
status: 'draft' | 'published' | 'archived';
visibility: 'public' | 'member' | 'confidential';
featured: boolean;
views: number;
likes: number;
comments: number;
publishedAt: string;
createdAt: string;
updatedAt: string;
seo?: {
metaTitle: string;
metaDescription: string;
keywords: string[];
};
}

interface NewsFilters {
category?: string;
status?: 'draft' | 'published' | 'archived';
visibility?: 'public' | 'member' | 'confidential';
featured?: boolean;
authorId?: string;
search?: string;
tags?: string[];
limit?: number;
offset?: number;
sortBy?: 'publishedAt' | 'title' | 'views';
sortOrder?: 'asc' | 'desc';
}

async function getNewsArticles(filters: NewsFilters = {}): Promise<{
data: NewsArticle[];
pagination: { limit: number; offset: number; total: number };
filters: NewsFilters;
}> {
const params = new URLSearchParams();

Object.entries(filters).forEach(([key, value]) => {
if (value !== undefined) {
if (Array.isArray(value)) {
params.append(key, value.join(','));
} else {
params.append(key, String(value));
}
}
});

const response = await fetch(`/api/news?${params.toString()}`);

if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to fetch news articles');
}

return response.json();
}

// Usage examples
try {
// Get featured articles
const featured = await getNewsArticles({ featured: true, limit: 5 });

// Search articles
const searchResults = await getNewsArticles({
search: 'blockchain',
category: 'technology'
});

// Get articles by tags
const taggedArticles = await getNewsArticles({
tags: ['platform', 'features'],
sortBy: 'views',
sortOrder: 'desc'
});

console.log('Featured articles:', featured.data.length);
} catch (error) {
console.error('Error:', error.message);
}

React Hook Example​

import { useState, useEffect } from 'react';

interface UseNewsArticlesResult {
articles: NewsArticle[];
loading: boolean;
error: string | null;
pagination: { limit: number; offset: number; total: number };
loadMore: () => void;
refresh: () => void;
}

function useNewsArticles(filters: NewsFilters = {}): UseNewsArticlesResult {
const [articles, setArticles] = useState<NewsArticle[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [pagination, setPagination] = useState({ limit: 10, offset: 0, total: 0 });

const fetchArticles = async (append = false) => {
try {
setLoading(true);
setError(null);

const currentOffset = append ? articles.length : 0;
const result = await getNewsArticles({
...filters,
offset: currentOffset
});

setArticles(prev => append ? [...prev, ...result.data] : result.data);
setPagination(result.pagination);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};

useEffect(() => {
fetchArticles();
}, [JSON.stringify(filters)]);

const loadMore = () => {
if (!loading && articles.length < pagination.total) {
fetchArticles(true);
}
};

const refresh = () => {
fetchArticles();
};

return { articles, loading, error, pagination, loadMore, refresh };
}

// Usage in component
function NewsListPage() {
const [filters, setFilters] = useState<NewsFilters>({
status: 'published',
limit: 12
});

const { articles, loading, error, pagination, loadMore } = useNewsArticles(filters);

const handleCategoryFilter = (category: string) => {
setFilters(prev => ({ ...prev, category, offset: 0 }));
};

const handleSearch = (search: string) => {
setFilters(prev => ({ ...prev, search, offset: 0 }));
};

if (loading && articles.length === 0) {
return <div>Loading news articles...</div>;
}

if (error) {
return <div>Error: {error}</div>;
}

return (
<div className="news-list">
<header>
<h1>Ring Platform News</h1>
<div className="filters">
<input
type="text"
placeholder="Search articles..."
onChange={(e) => handleSearch(e.target.value)}
/>
<select onChange={(e) => handleCategoryFilter(e.target.value)}>
<option value="">All Categories</option>
<option value="product-updates">Product Updates</option>
<option value="industry-news">Industry News</option>
<option value="company-spotlights">Company Spotlights</option>
</select>
</div>
</header>

<div className="articles-grid">
{articles.map(article => (
<article key={article.id} className="article-card">
{article.featuredImage && (
<img src={article.featuredImage} alt={article.title} />
)}
<div className="article-content">
<h2>{article.title}</h2>
<p>{article.excerpt}</p>
<div className="article-meta">
<span>By {article.authorName}</span>
<span>{new Date(article.publishedAt).toLocaleDateString()}</span>
<span>{article.views} views</span>
</div>
<div className="article-tags">
{article.tags.map(tag => (
<span key={tag} className="tag">#{tag}</span>
))}
</div>
</div>
</article>
))}
</div>

{articles.length < pagination.total && (
<button onClick={loadMore} disabled={loading}>
{loading ? 'Loading...' : 'Load More'}
</button>
)}
</div>
);
}

cURL Examples​

# Get all published articles
curl "https://ring.ck.ua/api/news?status=published&limit=10"

# Get featured articles
curl "https://ring.ck.ua/api/news?featured=true"

# Search articles
curl "https://ring.ck.ua/api/news?search=blockchain&category=technology"

# Get articles by tags
curl "https://ring.ck.ua/api/news?tags=platform,features&sortBy=views&sortOrder=desc"

# Get articles with pagination
curl "https://ring.ck.ua/api/news?limit=20&offset=40"

POST - Create News Article​

Authentication Requirements​

  • ADMIN Role: Only administrators can create news articles

Request Format​

POST /api/news
Content-Type: application/json
Authorization: Bearer <admin_jwt_token>

Request Body Schema​

interface CreateNewsRequest {
title: string; // Required: 1-200 characters
content: string; // Required: Article content
excerpt: string; // Required: 1-500 characters
slug?: string; // Optional: Auto-generated if not provided
category?: string; // Optional: Default 'other'
tags?: string[]; // Optional: Max 10 tags
featuredImage?: string; // Optional: Image URL
gallery?: string[]; // Optional: Gallery image URLs
status?: 'draft' | 'published'; // Optional: Default 'draft'
visibility?: 'public' | 'member' | 'confidential'; // Optional: Default 'public'
featured?: boolean; // Optional: Default false
seo?: {
metaTitle?: string;
metaDescription?: string;
keywords?: string[];
};
}

Example Request Body​

{
"title": "Ring Platform Introduces Advanced Analytics Dashboard",
"content": "We're excited to announce the launch of our new analytics dashboard that provides comprehensive insights into your professional network performance...",
"excerpt": "Discover powerful analytics tools to track your networking success and optimize your professional connections",
"category": "product-updates",
"tags": ["analytics", "dashboard", "insights", "networking"],
"featuredImage": "https://storage.ring.ck.ua/news/analytics-dashboard.jpg",
"status": "published",
"visibility": "public",
"featured": true,
"seo": {
"metaTitle": "Ring Platform Introduces Advanced Analytics Dashboard",
"metaDescription": "Discover powerful analytics tools to track your networking success",
"keywords": ["analytics", "dashboard", "networking", "insights"]
}
}

Response Format​

Success Response (200 OK)​

{
"success": true,
"data": {
"id": "news_987654321",
"title": "Ring Platform Introduces Advanced Analytics Dashboard",
"slug": "ring-platform-introduces-advanced-analytics-dashboard",
"content": "We're excited to announce the launch of our new analytics dashboard...",
"excerpt": "Discover powerful analytics tools to track your networking success",
"authorId": "admin_123456789",
"authorName": "Admin User",
"category": "product-updates",
"tags": ["analytics", "dashboard", "insights", "networking"],
"featuredImage": "https://storage.ring.ck.ua/news/analytics-dashboard.jpg",
"status": "published",
"visibility": "public",
"featured": true,
"views": 0,
"likes": 0,
"comments": 0,
"publishedAt": "2025-01-14T18:00:00Z",
"createdAt": "2025-01-14T18:00:00Z",
"updatedAt": "2025-01-14T18:00:00Z",
"seo": {
"metaTitle": "Ring Platform Introduces Advanced Analytics Dashboard",
"metaDescription": "Discover powerful analytics tools to track your networking success",
"keywords": ["analytics", "dashboard", "networking", "insights"]
}
},
"message": "News article created successfully"
}

Error Responses​

// 401 Unauthorized
{
"success": false,
"error": "Authentication required"
}

// 403 Forbidden
{
"success": false,
"error": "Admin access required"
}

// 400 Bad Request - Missing required fields
{
"success": false,
"error": "Title, content, and excerpt are required"
}

// 400 Bad Request - Duplicate slug
{
"success": false,
"error": "Article with this slug already exists"
}

Code Examples​

JavaScript/TypeScript​

async function createNewsArticle(articleData: CreateNewsRequest): Promise<NewsArticle> {
const response = await fetch('/api/news', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAdminToken()}`
},
body: JSON.stringify(articleData)
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to create news article');
}

const result = await response.json();
return result.data;
}

// Usage
try {
const newArticle = await createNewsArticle({
title: 'New Platform Feature Announcement',
content: 'We are excited to introduce...',
excerpt: 'Learn about our latest platform improvements',
category: 'product-updates',
tags: ['features', 'announcement'],
status: 'published',
featured: true
});

console.log('Article created:', newArticle.id);
} catch (error) {
console.error('Creation failed:', error.message);
}

React Admin Form Example​

import { useState } from 'react';

function CreateNewsForm() {
const [formData, setFormData] = useState<CreateNewsRequest>({
title: '',
content: '',
excerpt: '',
category: 'other',
tags: [],
status: 'draft',
visibility: 'public',
featured: false
});

const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);

try {
const article = await createNewsArticle(formData);
alert('Article created successfully!');
// Reset form or redirect
setFormData({
title: '',
content: '',
excerpt: '',
category: 'other',
tags: [],
status: 'draft',
visibility: 'public',
featured: false
});
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};

return (
<form onSubmit={handleSubmit} className="create-news-form">
<h2>Create News Article</h2>

<div className="form-group">
<label htmlFor="title">Title *</label>
<input
id="title"
type="text"
value={formData.title}
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
required
maxLength={200}
/>
</div>

<div className="form-group">
<label htmlFor="excerpt">Excerpt *</label>
<textarea
id="excerpt"
value={formData.excerpt}
onChange={(e) => setFormData(prev => ({ ...prev, excerpt: e.target.value }))}
required
maxLength={500}
rows={3}
/>
</div>

<div className="form-group">
<label htmlFor="content">Content *</label>
<textarea
id="content"
value={formData.content}
onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}
required
rows={10}
/>
</div>

<div className="form-row">
<div className="form-group">
<label htmlFor="category">Category</label>
<select
id="category"
value={formData.category}
onChange={(e) => setFormData(prev => ({ ...prev, category: e.target.value }))}
>
<option value="other">Other</option>
<option value="product-updates">Product Updates</option>
<option value="industry-news">Industry News</option>
<option value="company-spotlights">Company Spotlights</option>
<option value="technical">Technical</option>
</select>
</div>

<div className="form-group">
<label htmlFor="status">Status</label>
<select
id="status"
value={formData.status}
onChange={(e) => setFormData(prev => ({ ...prev, status: e.target.value as any }))}
>
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
</div>
</div>

<div className="form-group">
<label>
<input
type="checkbox"
checked={formData.featured}
onChange={(e) => setFormData(prev => ({ ...prev, featured: e.target.checked }))}
/>
Featured Article
</label>
</div>

{error && <div className="error">{error}</div>}

<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create Article'}
</button>
</form>
);
}

Advanced Filtering​

Search Functionality​

The search parameter performs full-text search across:

  • Article title
  • Excerpt content
  • Full article content
  • Tags

Tag Filtering​

Multiple tags can be specified using comma separation:

/api/news?tags=blockchain,defi,ethereum

Combined Filters​

Filters can be combined for precise results:

/api/news?category=technology&featured=true&search=blockchain&limit=5

Pagination Strategy​

Offset-Based Pagination​

// Page 1 (first 10 articles)
const page1 = await getNewsArticles({ limit: 10, offset: 0 });

// Page 2 (next 10 articles)
const page2 = await getNewsArticles({ limit: 10, offset: 10 });

// Page 3 (next 10 articles)
const page3 = await getNewsArticles({ limit: 10, offset: 20 });

Infinite Scroll Implementation​

function useInfiniteNews(filters: NewsFilters) {
const [allArticles, setAllArticles] = useState<NewsArticle[]>([]);
const [hasMore, setHasMore] = useState(true);

const loadMore = async () => {
const result = await getNewsArticles({
...filters,
offset: allArticles.length
});

setAllArticles(prev => [...prev, ...result.data]);
setHasMore(allArticles.length + result.data.length < result.pagination.total);
};

return { articles: allArticles, loadMore, hasMore };
}

SEO Optimization​

Automatic Slug Generation​

If no slug is provided, the system automatically generates one:

const slug = title
.toLowerCase()
.replace(/[^a-z0-9 -]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();

SEO Best Practices​

  • Meta Titles: 50-60 characters for optimal display
  • Meta Descriptions: 150-160 characters for search snippets
  • Keywords: 5-10 relevant keywords for content categorization
  • Featured Images: Optimized for social media sharing

Security Considerations​

  1. Admin-Only Creation: Only administrators can create/edit articles
  2. Input Validation: All content is validated and sanitized
  3. XSS Prevention: Content is properly escaped for display
  4. Rate Limiting: Prevents abuse with appropriate request limits
  5. Slug Uniqueness: Prevents URL conflicts and duplicate content

Changelog​

  • v1.0.0 - Initial implementation with basic listing and creation
  • v1.1.0 - Added advanced filtering and search capabilities
  • v1.2.0 - Enhanced SEO support and meta data
  • v1.3.0 - Added pagination and infinite scroll support
  • v1.4.0 - Improved security and admin controls