Introducing ContioReach - The Headless CMS Built for Content Excellence. Learn More

Next.js Integration Guide

This guide explains how to integrate Contioreach API with your Next.js application.

Setup

To use the Contioreach API in your Next.js application, you'll need to make HTTP requests directly to the API endpoints.

First, set up your environment variables in a .env.local file:

# For server components only API_KEY=your_api_key_here # For client components (if absolutely necessary) NEXT_PUBLIC_API_KEY=your_public_api_key_here

Client-Side Setup

For client components:

'use client';

import { useEffect, useState } from 'react';

export default function BlogPage() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        const response = await fetch('https://contioreach.com/api/public/post', {
          headers: {
            'x-api-key': process.env.NEXT_PUBLIC_API_KEY
          }
        });
        
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        
        const data = await response.json();
        setPosts(data.data);
      } catch (error) {
        console.error('Error fetching posts:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchPosts();
  }, []);

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <h1>Blog Posts</h1>
      <div className="grid gap-4">
        {posts.map((post) => (
          <article key={post.slug}>
            <h2>{post.title}</h2>
            <p>{post.excerpt}</p>
            {post.coverImage && (
              <img src={post.coverImage} alt={post.title} className="mt-2 rounded-lg" />
            )}
          </article>
        ))}
      </div>
    </div>
  );
}

Server-Side Setup

For server components and data fetching:

// app/blog/page.tsx
async function getPosts() {
  try {
    const response = await fetch('https://contioreach.com/api/public/post', {
      headers: {
        'x-api-key': process.env.API_KEY
      },
    });
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    const data = await response.json();
    return data.data;
  } catch (error) {
    console.error('Error fetching posts:', error);
    return [];
  }
}

export default async function BlogPage() {
  const posts = await getPosts();

  return (
    <div>
      <h1>Blog Posts</h1>
      <div className="grid gap-4">
        {posts.map((post) => (
          <article key={post.slug}>
            <h2>{post.title}</h2>
            <p>{post.excerpt}</p>
            {post.coverImage && (
              <img src={post.coverImage} alt={post.title} className="mt-2 rounded-lg" />
            )}
          </article>
        ))}
      </div>
    </div>
  );
}

Dynamic Routes for Blog Posts

Create dynamic routes for individual blog posts:

// app/blog/[slug]/page.tsx
async function getPost(slug: string) {
  try {
    // Using the slug parameter to fetch a specific post
    const response = await fetch('https://contioreach.com/api/public/post?slug=' + slug, {
      headers: {
        'x-api-key': process.env.API_KEY
      },
    });
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    const data = await response.json();
    return data.data; // For single post endpoint, this will be the post object
  } catch (error) {
    console.error('Error fetching post:', error);
    return null;
  }
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug);

  if (!post) {
    return <div>Post not found</div>;
  }

  return (
    <article>
      <h1 className="text-3xl font-bold">{post.title}</h1>
      {post.coverImage && (
        <img 
          src={post.coverImage} 
          alt={post.title} 
          className="mt-4 rounded-lg max-h-96 object-cover" 
        />
      )}
      <div className="mt-4 text-sm text-gray-500">
        Published on {new Date(post.published_at).toLocaleDateString()}
      </div>
      
      {post.authors && post.authors.length > 0 && (
        <div className="mt-2 flex gap-2">
          By: 
          {post.authors.map((author) => (
            <span key={author.slug}>{author.name}</span>
          ))}
        </div>
      )}
      
      {post.categories && post.categories.length > 0 && (
        <div className="mt-2 flex flex-wrap gap-2">
          {post.categories.map(category => (
            <span key={category.slug} className="px-2 py-1 bg-slate-100 rounded-md text-sm">
              {category.name}
            </span>
          ))}
        </div>
      )}
      
      <div 
        className="mt-8 prose prose-lg"
        dangerouslySetInnerHTML={{ __html: post.html_content }} 
      />
    </article>
  );
}

API Route Setup

For better security, you can create an API route to handle requests to the Contioreach API. This keeps your API key on the server side and prevents exposure in client-side code.

// app/api/posts/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const category = searchParams.get('category');
  const slug = searchParams.get('slug');
  const limit = searchParams.get('limit') || '10';
  
  // Build the query parameters
  const queryParams = new URLSearchParams();
  if (category) queryParams.set('category', category);
  if (slug) queryParams.set('slug', slug);
  queryParams.set('limit', limit);
  
  try {
    const response = await fetch(
      'https://contioreach.com/api/public/post?' + queryParams.toString(), 
      {
        headers: {
          'x-api-key': process.env.API_KEY || '',
        },
      }
    );
    
    if (!response.ok) {
      throw new Error('Failed to fetch from Contioreach API');
    }
    
    const data = await response.json();
    return NextResponse.json(data);
  } catch (error) {
    console.error('Error in API route:', error);
    return NextResponse.json(
      { error: 'Failed to fetch posts' }, 
      { status: 500 }
    );
  }
}

Then in your client component, fetch from your API route instead:

'use client';

import { useEffect, useState } from 'react';

export default function BlogPage() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchPosts() {
      try {
        // Fetch from your own API route - no API key needed in client code
        const response = await fetch('/api/posts?limit=10&category=news');
        const data = await response.json();
        setPosts(data.data);
      } catch (error) {
        console.error('Error fetching posts:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchPosts();
  }, []);
  
  // Component rendering code...
}