You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node.js+Express项目中实现Pexels API分页加载视频

Solution for Loading More Videos with "Show More" Button

Here's a step-by-step implementation to add paginated loading via a button to your Node.js + Express + EJS app:

1. Backend: Add an API Endpoint for Paginated Videos

First, we'll create a dedicated API route that accepts a page parameter and returns video data as JSON. This lets us fetch new pages without reloading the entire page.

Update app.js:

If using the official Pexels npm package:

const express = require('express');
const app = express();
const { createClient } = require('pexels');
require('dotenv').config(); // Store API keys securely with dotenv

const pexelsClient = createClient(process.env.PEXELS_API_KEY);

// Initial homepage route (renders first page of videos)
app.get('/', async (req, res) => {
  try {
    const response = await pexelsClient.videos.search({
      query: 'nature',
      page: 1,
      per_page: 8
    });
    res.render('index', {
      videos: response.videos,
      totalResults: response.total_results // Pass total count to frontend
    });
  } catch (err) {
    console.error('Error fetching initial videos:', err);
    res.render('index', { videos: [], totalResults: 0 });
  }
});

// New API route for fetching additional pages
app.get('/api/videos', async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const response = await pexelsClient.videos.search({
      query: 'nature',
      page: page,
      per_page: 8
    });
    res.json({
      videos: response.videos,
      totalResults: response.total_results
    });
  } catch (err) {
    console.error('Error fetching videos via API:', err);
    res.status(500).json({ error: 'Failed to load videos' });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

If using raw fetch (no npm package):

const express = require('express');
const app = express();
const fetch = require('node-fetch'); // Use native fetch in Node.js 18+
require('dotenv').config();

const PEXELS_API_KEY = process.env.PEXELS_API_KEY;
const PEXELS_VIDEO_URL = 'https://api.pexels.com/videos/search';

app.get('/', async (req, res) => {
  try {
    const response = await fetch(`${PEXELS_VIDEO_URL}?query=nature&page=1&per_page=8`, {
      headers: { Authorization: PEXELS_API_KEY }
    });
    const data = await response.json();
    res.render('index', {
      videos: data.videos,
      totalResults: data.total_results
    });
  } catch (err) {
    console.error('Error:', err);
    res.render('index', { videos: [], totalResults: 0 });
  }
});

app.get('/api/videos', async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const response = await fetch(`${PEXELS_VIDEO_URL}?query=nature&page=${page}&per_page=8`, {
      headers: { Authorization: PEXELS_API_KEY }
    });
    const data = await response.json();
    res.json({ videos: data.videos, totalResults: data.total_results });
  } catch (err) {
    res.status(500).json({ error: 'Failed to load videos' });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Important: Store your Pexels API key in a .env file (never hardcode it!) and install dotenv via npm install dotenv.

2. Frontend: Add "Show More" Button & AJAX Logic

Update your index.ejs to include the button and JavaScript that fetches new pages and appends videos to the DOM.

index.ejs:

<!DOCTYPE html>
<html>
<head>
  <title>Nature Videos</title>
  <style>
    .videos-container {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
      gap: 20px;
      margin-bottom: 20px;
      padding: 0 20px;
    }
    .video-card {
      border: 1px solid #eee;
      border-radius: 8px;
      overflow: hidden;
      transition: transform 0.2s;
    }
    .video-card:hover {
      transform: translateY(-5px);
    }
    .video-card img {
      width: 100%;
      height: 180px;
      object-fit: cover;
    }
    .video-card p {
      padding: 10px;
      margin: 0;
      font-size: 14px;
      color: #333;
    }
    #show-more-btn {
      display: block;
      margin: 0 auto 30px;
      padding: 10px 20px;
      background: #007bff;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
    }
    #show-more-btn:disabled {
      background: #ccc;
      cursor: not-allowed;
    }
  </style>
</head>
<body>
  <div class="videos-container">
    <% videos.forEach(video => { %>
      <div class="video-card">
        <img src="<%= video.image %>" alt="Video thumbnail">
        <p>By <%= video.user.name %></p>
      </div>
    <% }) %>
  </div>

  <button id="show-more-btn" <%= Math.ceil(totalResults /8) <=1 ? 'disabled' : '' %>>
    <%= Math.ceil(totalResults /8) <=1 ? 'No more videos' : 'Show More' %>
  </button>

  <script>
    let currentPage = 1;
    const totalResults = <%= totalResults %>;
    const perPage = 8;
    const maxPages = Math.ceil(totalResults / perPage);
    
    const showMoreBtn = document.getElementById('show-more-btn');
    const videosContainer = document.querySelector('.videos-container');

    showMoreBtn.addEventListener('click', async () => {
      currentPage++;
      showMoreBtn.disabled = true;
      showMoreBtn.textContent = 'Loading...';

      try {
        const response = await fetch(`/api/videos?page=${currentPage}`);
        if (!response.ok) throw new Error('Request failed');
        
        const { videos } = await response.json();

        // Append new videos to the container
        videos.forEach(video => {
          const card = document.createElement('div');
          card.className = 'video-card';
          card.innerHTML = `
            <img src="${video.image}" alt="Video thumbnail">
            <p>By ${video.user.name}</p>
          `;
          videosContainer.appendChild(card);
        });

        // Disable button if we've reached the last page
        if (currentPage >= maxPages) {
          showMoreBtn.textContent = 'No more videos';
          showMoreBtn.disabled = true;
        } else {
          showMoreBtn.textContent = 'Show More';
          showMoreBtn.disabled = false;
        }
      } catch (err) {
        console.error('Error loading videos:', err);
        showMoreBtn.textContent = 'Show More';
        showMoreBtn.disabled = false;
        alert('Failed to load more videos. Please try again later.');
      }
    });
  </script>
</body>
</html>

3. Key Notes & Improvements

  • UX Enhancements: The button shows a loading state while fetching, and disables automatically when all pages are loaded.
  • Error Handling: We catch errors on both backend and frontend, providing clear feedback to users.
  • Consistent Styling: Dynamically added video cards match the initial ones to keep your UI uniform.
  • Security: Sensitive API keys are stored in environment variables instead of being hardcoded.

内容的提问来源于stack exchange,提问作者r121

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:22:52