Elysia Tools

Durchsuche Tools, Beispiele, visuelle Erklärungen und Themenguides an einem klaren Ort.

Beliebte Kategorien

Themen

Sprache
ZHDERUPTENFRES

Elysia Tools

Mobile Navigation

CMS

Strapi Headless CMS Beispiele

Umfassende Strapi Beispiele mit Content Modeling, API Integration, Authentifizierung, Plugins und Deployment Patterns

Beispiele

Einträge in dieser Sammlung

QuellcodejavascriptMittel

Strapi Content Modeling

Vollständige Content-Type-Definitionen, Beziehungen und Feldkonfigurationen für verschiedene Anwendungsfälle

Schwierigkeit

6/10

Geschätzte Zeit

35 min

Schlagwörter

strapi, content modeling, cms

Voraussetzungen

Strapi basics, Content modeling concepts, JSON schema, Database concepts

javascript
// Strapi Content Modeling Examples
// File: config/api/article/content-types/article/schema.json

{
  "kind": "collectionType",
  "collectionName": "articles",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article",
    "description": "Blog articles with rich content and media"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {
    "i18n": {
      "localized": true
    }
  },
  "attributes": {
    "title": {
      "type": "string",
      "required": true,
      "maxLength": 255,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "slug": {
      "type": "uid",
      "targetField": "title",
      "required": true
    },
    "excerpt": {
      "type": "text",
      "maxLength": 500,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "content": {
      "type": "richtext",
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "featured_image": {
      "type": "media",
      "multiple": false,
      "required": false,
      "allowedTypes": ["images"]
    },
    "gallery": {
      "type": "media",
      "multiple": true,
      "required": false,
      "allowedTypes": ["images"]
    },
    "category": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "api::category.category"
    },
    "tags": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::tag.tag"
    },
    "author": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "api::author.author"
    },
    "seo": {
      "type": "component",
      "repeatable": false,
      "component": "shared.seo"
    },
    "published_at": {
      "type": "datetime",
      "configurable": false,
      "writable": false,
      "default": "now"
    },
    "reading_time": {
      "type": "integer",
      "min": 0
    },
    "view_count": {
      "type": "integer",
      "default": 0,
      "private": true
    }
  }
}

// File: config/api/category/content-types/category/schema.json

{
  "kind": "collectionType",
  "collectionName": "categories",
  "info": {
    "singularName": "category",
    "pluralName": "categories",
    "displayName": "Category",
    "description": "Article categories for content organization"
  },
  "options": {
    "draftAndPublish": false
  },
  "pluginOptions": {
    "i18n": {
      "localized": true
    }
  },
  "attributes": {
    "name": {
      "type": "string",
      "required": true,
      "maxLength": 100,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "slug": {
      "type": "uid",
      "targetField": "name",
      "required": true
    },
    "description": {
      "type": "text",
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "color": {
      "type": "string",
      "regex": "^#[0-9A-Fa-f]{6}$"
    },
    "icon": {
      "type": "media",
      "multiple": false,
      "required": false,
      "allowedTypes": ["images"]
    },
    "parent": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "api::category.category",
      "nullable": true
    },
    "children": {
      "type": "relation",
      "relation": "oneToMany",
      "target": "api::category.category",
      "mappedBy": "parent",
      "private": true
    },
    "articles_count": {
      "type": "integer",
      "default": 0,
      "private": true
    }
  }
}

// File: config/api/product/content-types/product/schema.json

{
  "kind": "collectionType",
  "collectionName": "products",
  "info": {
    "singularName": "product",
    "pluralName": "products",
    "displayName": "Product",
    "description": "E-commerce products with variants and pricing"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {
    "i18n": {
      "localized": true
    }
  },
  "attributes": {
    "name": {
      "type": "string",
      "required": true,
      "maxLength": 255,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "slug": {
      "type": "uid",
      "targetField": "name",
      "required": true
    },
    "description": {
      "type": "richtext",
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "short_description": {
      "type": "text",
      "maxLength": 500,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "sku": {
      "type": "string",
      "required": true,
      "unique": true,
      "regex": "^[A-Z0-9-]+$"
    },
    "price": {
      "type": "decimal",
      "required": true,
      "min": 0
    },
    "compare_price": {
      "type": "decimal",
      "min": 0
    },
    "cost": {
      "type": "decimal",
      "min": 0,
      "private": true
    },
    "images": {
      "type": "media",
      "multiple": true,
      "required": false,
      "allowedTypes": ["images"]
    },
    "variants": {
      "type": "component",
      "repeatable": true,
      "component": "product.product-variant"
    },
    "inventory": {
      "type": "component",
      "repeatable": false,
      "component": "product.inventory"
    },
    "shipping": {
      "type": "component",
      "repeatable": false,
      "component": "product.shipping"
    },
    "seo": {
      "type": "component",
      "repeatable": false,
      "component": "shared.seo"
    },
    "categories": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::product-category.product-category"
    },
    "brand": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "api::brand.brand"
    },
    "tags": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::tag.tag"
    },
    "attributes": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::product-attribute.product-attribute"
    },
    "reviews": {
      "type": "relation",
      "relation": "oneToMany",
      "target": "api::review.review",
      "mappedBy": "product"
    },
    "featured": {
      "type": "boolean",
      "default": false
    },
    "available": {
      "type": "boolean",
      "default": true
    }
  }
}

// File: config/components/product/product-variant.json

{
  "collectionName": "components_product_variants",
  "info": {
    "displayName": "Product Variant",
    "icon": "cube",
    "description": "Product variants with different options and pricing"
  },
  "options": {},
  "attributes": {
    "name": {
      "type": "string",
      "required": true,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "sku": {
      "type": "string",
      "required": true,
      "unique": true
    },
    "price": {
      "type": "decimal",
      "required": true
    },
    "compare_price": {
      "type": "decimal"
    },
    "weight": {
      "type": "decimal"
    },
    "inventory": {
      "type": "integer",
      "default": 0
    },
    "options": {
      "type": "json",
      "default": {}
    },
    "image": {
      "type": "media",
      "multiple": false,
      "required": false,
      "allowedTypes": ["images"]
    }
  }
}

// File: config/components/shared/seo.json

{
  "collectionName": "components_shared_seos",
  "info": {
    "displayName": "SEO",
    "icon": "search",
    "description": "SEO metadata for search engine optimization"
  },
  "options": {},
  "pluginOptions": {
    "i18n": {
      "localized": true
    }
  },
  "attributes": {
    "meta_title": {
      "type": "string",
      "maxLength": 60,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "meta_description": {
      "type": "text",
      "maxLength": 160,
      "pluginOptions": {
        "i18n": {
          "localized": true
        }
      }
    },
    "keywords": {
      "type": "text"
    },
    "canonical_url": {
      "type": "string"
    },
    "og_image": {
      "type": "media",
      "multiple": false,
      "required": false,
      "allowedTypes": ["images"]
    },
    "structured_data": {
      "type": "json"
    },
    "meta_robots": {
      "type": "enumeration",
      "enum": ["index", "noindex", "follow", "nofollow"],
      "default": "index,follow"
    }
  }
}

// File: database/seeds/01-categories.js

module.exports = {
  async up(strapi) {
    // Create main categories
    const categories = [
      {
        name: 'Technology',
        slug: 'technology',
        description: 'Articles about technology and programming',
        color: '#3B82F6'
      },
      {
        name: 'Design',
        slug: 'design',
        description: 'Design articles and tutorials',
        color: '#8B5CF6'
      },
      {
        name: 'Business',
        slug: 'business',
        description: 'Business and entrepreneurship articles',
        color: '#10B981'
      },
      {
        name: 'Marketing',
        slug: 'marketing',
        description: 'Marketing strategies and tips',
        color: '#F59E0B'
      }
    ];

    for (const category of categories) {
      await strapi.query('api::category.category').create({
        data: category
      });
    }
  },

  async down(strapi) {
    // Remove seeded categories
    await strapi.query('api::category.category').deleteMany({
      where: {
        slug: ['technology', 'design', 'business', 'marketing']
      }
    });
  }
};

// File: database/seeds/02-tags.js

module.exports = {
  async up(strapi) {
    const tags = [
      { name: 'JavaScript', slug: 'javascript' },
      { name: 'React', slug: 'react' },
      { name: 'Node.js', slug: 'nodejs' },
      { name: 'CSS', slug: 'css' },
      { name: 'HTML', slug: 'html' },
      { name: 'TypeScript', slug: 'typescript' },
      { name: 'Vue.js', slug: 'vuejs' },
      { name: 'Angular', slug: 'angular' },
      { name: 'Python', slug: 'python' },
      { name: 'Machine Learning', slug: 'machine-learning' },
      { name: 'UI/UX', slug: 'ui-ux' },
      { name: 'Startup', slug: 'startup' },
      { name: 'SEO', slug: 'seo' },
      { name: 'Analytics', slug: 'analytics' },
      { name: 'E-commerce', slug: 'ecommerce' }
    ];

    for (const tag of tags) {
      await strapi.query('api::tag.tag').create({
        data: tag
      });
    }
  },

  async down(strapi) {
    const slugs = tags.map(tag => tag.slug);
    await strapi.query('api::tag.tag').deleteMany({
      where: {
        slug: slugs
      }
    });
  }
};

// File: database/seeds/03-authors.js

module.exports = {
  async up(strapi) {
    const authors = [
      {
        name: 'John Doe',
        email: 'john@example.com',
        bio: 'Full-stack developer with 10+ years of experience',
        avatar: null,
        social: {
          twitter: 'johndoe',
          github: 'johndoe',
          linkedin: 'john-doe'
        }
      },
      {
        name: 'Jane Smith',
        email: 'jane@example.com',
        bio: 'UX designer passionate about user experience',
        avatar: null,
        social: {
          twitter: 'janesmith',
          dribbble: 'janesmith',
          behance: 'jane-smith'
        }
      }
    ];

    for (const author of authors) {
      await strapi.query('api::author.author').create({
        data: author
      });
    }
  },

  async down(strapi) {
    await strapi.query('api::author.author').deleteMany();
  }
};
QuellcodejavascriptKomplex

Strapi API Integration

Vollständige API-Integrationsbeispiele mit REST und GraphQL Abfragen, Authentifizierung und Frontend-Implementierung

Schwierigkeit

8/10

Geschätzte Zeit

40 min

Schlagwörter

strapi, api, integration, frontend

Voraussetzungen

Strapi basics, REST/GraphQL APIs, React hooks, JavaScript ES6+, Authentication concepts

javascript
// Strapi API Integration Examples
// File: lib/strapi.js - API Client Setup

class StrapiClient {
  constructor(baseURL, token = null) {
    this.baseURL = baseURL;
    this.token = token;
    this.cache = new Map();
  }

  setToken(token) {
    this.token = token;
  }

  async request(endpoint, options = {}) {
    const url = new URL(endpoint, this.baseURL);

    const config = {
      headers: {
        'Content-Type': 'application/json',
        ...options.headers,
      },
      ...options,
    };

    if (this.token) {
      config.headers.Authorization = `Bearer ${this.token}`;
    }

    try {
      const response = await fetch(url.toString(), config);

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      console.error('Strapi API Error:', error);
      throw error;
    }
  }

  async get(endpoint, params = {}) {
    const searchParams = new URLSearchParams();

    Object.keys(params).forEach(key => {
      if (Array.isArray(params[key])) {
        params[key].forEach(value => {
          searchParams.append(`${key}[]`, value);
        });
      } else if (params[key] !== undefined && params[key] !== null) {
        searchParams.append(key, params[key]);
      }
    });

    const queryString = searchParams.toString();
    const url = queryString ? `${endpoint}?${queryString}` : endpoint;

    // Check cache first
    const cacheKey = `GET_${url}`;
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }

    const data = await this.request(url);

    // Cache for 5 minutes
    this.cache.set(cacheKey, data);
    setTimeout(() => this.cache.delete(cacheKey), 5 * 60 * 1000);

    return data;
  }

  async post(endpoint, data) {
    return this.request(endpoint, {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }

  async put(endpoint, data) {
    return this.request(endpoint, {
      method: 'PUT',
      body: JSON.stringify(data),
    });
  }

  async delete(endpoint) {
    return this.request(endpoint, {
      method: 'DELETE',
    });
  }

  clearCache() {
    this.cache.clear();
  }
}

// Create and export client instances
export const strapiClient = new StrapiClient(
  process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337'
);

export const strapiAdminClient = new StrapiClient(
  process.env.STRAPI_ADMIN_URL || 'http://localhost:1337',
  process.env.STRAPI_ADMIN_TOKEN
);

// File: lib/strapi-queries.js - Predefined Queries

export const strapiQueries = {
  // Articles with population
  getArticles: (params = {}) => ({
    endpoint: '/articles',
    params: {
      populate: ['*', 'featured_image', 'category', 'tags', 'author.avatar'],
      sort: 'publishedAt:desc',
      publicationState: 'live',
      ...params,
    },
  }),

  getArticleBySlug: (slug, params = {}) => ({
    endpoint: '/articles',
    params: {
      filters: { slug: { $eq: slug } },
      populate: ['*', 'featured_image', 'category', 'tags', 'author.avatar'],
      publicationState: 'live',
      ...params,
    },
  }),

  getRelatedArticles: (articleId, categoryId, limit = 3) => ({
    endpoint: '/articles',
    params: {
      filters: {
        id: { $ne: articleId },
        category: { id: { $eq: categoryId } },
      },
      populate: ['featured_image', 'category'],
      sort: 'publishedAt:desc',
      pagination: { limit, start: 0 },
      publicationState: 'live',
    },
  }),

  getCategories: () => ({
    endpoint: '/categories',
    params: {
      populate: ['parent'],
      sort: 'name:asc',
    },
  }),

  getProducts: (params = {}) => ({
    endpoint: '/products',
    params: {
      populate: ['*', 'images', 'variants.image', 'categories', 'brand', 'reviews'],
      sort: 'createdAt:desc',
      publicationState: 'live',
      ...params,
    },
  }),

  getProductsByCategory: (categoryId, params = {}) => ({
    endpoint: '/products',
    params: {
      filters: {
        categories: { id: { $eq: categoryId } },
      },
      populate: ['*', 'images', 'categories'],
      sort: 'createdAt:desc',
      publicationState: 'live',
      ...params,
    },
  }),

  searchProducts: (searchTerm, params = {}) => ({
    endpoint: '/products',
    params: {
      filters: {
        $or: [
          { name: { $containsi: searchTerm } },
          { description: { $containsi: searchTerm } },
          { short_description: { $containsi: searchTerm } },
          { sku: { $containsi: searchTerm } },
        ],
      },
      populate: ['images', 'categories'],
      sort: 'name:asc',
      publicationState: 'live',
      ...params,
    },
  }),

  // User-specific queries
  getUserOrders: (userId, params = {}) => ({
    endpoint: '/orders',
    params: {
      filters: { user: { id: { $eq: userId } } },
      populate: ['items.product', 'items.variant', 'shipping_address', 'billing_address'],
      sort: 'createdAt:desc',
      ...params,
    },
  }),

  createUserCart: (userId) => ({
    endpoint: '/carts',
    params: {
      filters: { user: { id: { $eq: userId } } },
      populate: ['items.product', 'items.variant'],
    },
  }),
};

// File: hooks/useStrapi.js - React Hooks

import { useState, useEffect, useCallback } from 'react';
import { strapiClient } from '../lib/strapi';

export function useStrapiQuery(query, dependencies = []) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchData = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);

      const response = await strapiClient.get(query.endpoint, query.params);
      setData(response);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }, [query.endpoint, query.params]);

  useEffect(() => {
    fetchData();
  }, [fetchData, ...dependencies]);

  const refetch = useCallback(() => {
    return fetchData();
  }, [fetchData]);

  return { data, loading, error, refetch };
}

export function useStrapiMutation(endpoint, method = 'POST') {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const mutate = useCallback(async (data) => {
    try {
      setLoading(true);
      setError(null);

      let response;
      switch (method) {
        case 'POST':
          response = await strapiClient.post(endpoint, data);
          break;
        case 'PUT':
          response = await strapiClient.put(endpoint, data);
          break;
        case 'DELETE':
          response = await strapiClient.delete(endpoint);
          break;
        default:
          throw new Error(`Unsupported method: ${method}`);
      }

      return response;
    } catch (err) {
      setError(err);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [endpoint, method]);

  return { mutate, loading, error };
}

// File: services/article-service.js - Service Layer

import { strapiClient, strapiQueries } from '../lib/strapi';

export class ArticleService {
  static async getArticles(params = {}) {
    const query = strapiQueries.getArticles(params);
    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      articles: response.data,
      pagination: response.meta.pagination,
    };
  }

  static async getArticleBySlug(slug) {
    const query = strapiQueries.getArticleBySlug(slug);
    const response = await strapiClient.get(query.endpoint, query.params);

    return response.data?.[0] || null;
  }

  static async getRelatedArticles(articleId, categoryId, limit = 3) {
    const query = strapiQueries.getRelatedArticles(articleId, categoryId, limit);
    const response = await strapiClient.get(query.endpoint, query.params);

    return response.data;
  }

  static async incrementViewCount(articleId) {
    return strapiClient.put(`/articles/${articleId}/view-count`);
  }

  static async searchArticles(searchTerm, params = {}) {
    const query = {
      endpoint: '/articles',
      params: {
        filters: {
          $or: [
            { title: { $containsi: searchTerm } },
            { content: { $containsi: searchTerm } },
            { excerpt: { $containsi: searchTerm } },
          ],
        },
        populate: ['featured_image', 'category', 'author'],
        sort: 'publishedAt:desc',
        publicationState: 'live',
        ...params,
      },
    };

    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      articles: response.data,
      pagination: response.meta.pagination,
    };
  }

  static async getArticlesByTag(tagSlug, params = {}) {
    const query = {
      endpoint: '/articles',
      params: {
        filters: {
          tags: { slug: { $eq: tagSlug } },
        },
        populate: ['featured_image', 'category', 'tags', 'author'],
        sort: 'publishedAt:desc',
        publicationState: 'live',
        ...params,
      },
    };

    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      articles: response.data,
      pagination: response.meta.pagination,
    };
  }
}

// File: services/product-service.js - Product Service

export class ProductService {
  static async getProducts(params = {}) {
    const query = strapiQueries.getProducts(params);
    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      products: response.data,
      pagination: response.meta.pagination,
    };
  }

  static async getProductBySlug(slug) {
    const query = {
      endpoint: '/products',
      params: {
        filters: { slug: { $eq: slug } },
        populate: ['*', 'images', 'variants.image', 'categories', 'brand', 'reviews'],
        publicationState: 'live',
      },
    };

    const response = await strapiClient.get(query.endpoint, query.params);

    return response.data?.[0] || null;
  }

  static async searchProducts(searchTerm, params = {}) {
    const query = strapiQueries.searchProducts(searchTerm, params);
    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      products: response.data,
      pagination: response.meta.pagination,
    };
  }

  static async getProductsByCategory(categorySlug, params = {}) {
    const query = {
      endpoint: '/products',
      params: {
        filters: {
          categories: { slug: { $eq: categorySlug } },
        },
        populate: ['images', 'categories'],
        sort: 'createdAt:desc',
        publicationState: 'live',
        ...params,
      },
    };

    const response = await strapiClient.get(query.endpoint, query.params);

    return {
      products: response.data,
      pagination: response.meta.pagination,
    };
  }

  static async getFeaturedProducts(limit = 8) {
    const query = strapiQueries.getProducts({
      filters: { featured: true },
      pagination: { limit },
    });

    const response = await strapiClient.get(query.endpoint, query.params);

    return response.data;
  }
}

// File: components/ArticleList.js - Frontend Component Example

import React, { useState, useEffect } from 'react';
import { ArticleService } from '../services/article-service';

export default function ArticleList({ category, tag, limit = 10 }) {
  const [articles, setArticles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [page, setPage] = useState(1);
  const [pagination, setPagination] = useState(null);

  useEffect(() => {
    fetchArticles();
  }, [category, tag, page]);

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

      const params = {
        pagination: { limit, page },
      };

      if (category) {
        params.filters = { category: { slug: { $eq: category } } };
      }

      if (tag) {
        params.filters = {
          ...params.filters,
          tags: { slug: { $eq: tag } },
        };
      }

      const result = await ArticleService.getArticles(params);
      setArticles(result.articles);
      setPagination(result.pagination);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const handlePageChange = (newPage) => {
    setPage(newPage);
  };

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

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

  return (
    <div>
      <h1>Articles</h1>
      <div className="article-grid">
        {articles.map((article) => (
          <ArticleCard key={article.id} article={article} />
        ))}
      </div>

      {pagination && (
        <Pagination
          currentPage={page}
          totalPages={pagination.pageCount}
          onPageChange={handlePageChange}
        />
      )}
    </div>
  );
}

// File: lib/strapi-graphql.js - GraphQL Client

import { GraphQLClient, gql } from 'graphql-request';

class StrapiGraphQLClient {
  constructor(baseURL, token = null) {
    this.client = new GraphQLClient(`${baseURL}/graphql`, {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    });
  }

  async query(query, variables = {}) {
    try {
      return await this.client.request(query, variables);
    } catch (error) {
      console.error('Strapi GraphQL Error:', error);
      throw error;
    }
  }

  async mutation(mutation, variables = {}) {
    try {
      return await this.client.request(mutation, variables);
    } catch (error) {
      console.error('Strapi GraphQL Error:', error);
      throw error;
    }
  }
}

export const strapiGraphQL = new StrapiGraphQLClient(
  process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337',
  process.env.STRAPI_API_TOKEN
);

// GraphQL Queries
export const GET_ARTICLES = gql`
  query GetArticles(
    $pagination: PaginationArg = { page: 1, pageSize: 10 }
    $filters: ArticleFiltersInput = {}
    $sort: [String] = ["publishedAt:desc"]
    $publicationState: PublicationState = LIVE
  ) {
    articles(pagination: $pagination, filters: $filters, sort: $sort, publicationState: $publicationState) {
      data {
        id
        attributes {
          title
          slug
          excerpt
          publishedAt
          featured_image {
            data {
              attributes {
                url
                alternativeText
              }
            }
          }
          category {
            data {
              attributes {
                name
                slug
                color
              }
            }
          }
          tags {
            data {
              attributes {
                name
                slug
              }
            }
          }
          author {
            data {
              attributes {
                name
                avatar {
                  data {
                    attributes {
                      url
                    }
                  }
                }
              }
            }
          }
        }
      }
      meta {
        pagination {
          page
          pageSize
          pageCount
          total
        }
      }
    }
  }
`;

export const GET_ARTICLE_BY_SLUG = gql`
  query GetArticleBySlug($slug: String!, $publicationState: PublicationState = LIVE) {
    articles(filters: { slug: { eq: $slug } }, publicationState: $publicationState) {
      data {
        id
        attributes {
          title
          slug
          content
          publishedAt
          featured_image {
            data {
              attributes {
                url
                alternativeText
                caption
              }
            }
          }
          category {
            data {
              attributes {
                name
                slug
                description
              }
            }
          }
          tags {
            data {
              attributes {
                name
                slug
              }
            }
          }
          author {
            data {
              attributes {
                name
                bio
                avatar {
                  data {
                    attributes {
                      url
                    }
                  }
                }
              }
            }
          }
          seo {
            meta_title
            meta_description
            keywords
          }
        }
      }
    }
  }
`;
QuellcodejavascriptKomplex

Strapi Plugins & Deployment

Vollständige Plugin-Entwicklung, Anpassung und Deployment-Strategien für Produktionsumgebungen

Schwierigkeit

9/10

Geschätzte Zeit

50 min

Schlagwörter

strapi, plugins, deployment, production

Voraussetzungen

Strapi basics, Plugin development, Docker, DevOps, Database concepts

javascript
// Strapi Plugins & Deployment Examples
// File: plugins/custom-controllers/server/controllers/custom.js

'use strict';

/**
 * Custom controller example
 */

module.exports = {
  // Custom route handler
  async customAction(ctx) {
    try {
      const { param } = ctx.params;
      const { query } = ctx.query;

      // Custom business logic
      const result = await strapi.service('api::custom.custom').processCustomData(param, query);

      ctx.send({
        message: 'Custom action completed successfully',
        data: result,
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      ctx.status = 500;
      ctx.send({
        message: 'Custom action failed',
        error: error.message
      });
    }
  },

  // Bulk operations
  async bulkUpdate(ctx) {
    try {
      const { ids, data } = ctx.request.body;

      // Validate input
      if (!Array.isArray(ids) || ids.length === 0) {
        return ctx.badRequest('Invalid IDs array');
      }

      // Process bulk update
      const results = await strapi.service('api::custom.custom').bulkUpdate(ids, data);

      ctx.send({
        message: `Successfully updated ${results.updated} items`,
        updated: results.updated,
        failed: results.failed
      });
    } catch (error) {
      ctx.status = 500;
      ctx.send({
        message: 'Bulk update failed',
        error: error.message
      });
    }
  },

  // Export data
  async export(ctx) {
    try {
      const { format = 'json', filters = {} } = ctx.query;

      const data = await strapi.service('api::custom.custom').exportData(filters);

      if (format === 'csv') {
        ctx.set('Content-Type', 'text/csv');
        ctx.set('Content-Disposition', 'attachment; filename="export.csv"');
        return ctx.send(data.csv);
      } else {
        ctx.set('Content-Type', 'application/json');
        ctx.set('Content-Disposition', 'attachment; filename="export.json"');
        return ctx.send(data.json);
      }
    } catch (error) {
      ctx.status = 500;
      ctx.send({
        message: 'Export failed',
        error: error.message
      });
    }
  }
};

// File: plugins/custom-controllers/server/services/custom.js

'use strict';

/**
 * Custom service example
 */

module.exports = ({ strapi }) => ({
  // Process custom data
  async processCustomData(param, query = {}) {
    // Custom business logic
    const entities = await strapi.query('api::entity.entity').findMany({
      where: {
        customField: param,
        ...query.filters
      },
      populate: ['relations']
    });

    // Transform data
    const processedData = entities.map(entity => ({
      ...entity,
      processedValue: this.calculateProcessedValue(entity),
      metadata: this.generateMetadata(entity)
    }));

    return {
      count: processedData.length,
      data: processedData
    };
  },

  // Calculate processed value
  calculateProcessedValue(entity) {
    // Example calculation
    return entity.field1 * entity.field2 + entity.field3;
  },

  // Generate metadata
  generateMetadata(entity) {
    return {
      createdAt: entity.createdAt,
      updatedAt: entity.updatedAt,
      version: this.getEntityVersion(entity)
    };
  },

  // Get entity version
  getEntityVersion(entity) {
    return `v${entity.id}.${entity.updatedAt.getTime()}`;
  },

  // Bulk update implementation
  async bulkUpdate(ids, data) {
    const results = {
      updated: 0,
      failed: []
    };

    for (const id of ids) {
      try {
        await strapi.query('api::entity.entity').update({
          where: { id },
          data: {
            ...data,
            updatedBy: 1 // Current user ID
          }
        });
        results.updated++;
      } catch (error) {
        results.failed.push({
          id,
          error: error.message
        });
      }
    }

    return results;
  },

  // Export data implementation
  async exportData(filters = {}) {
    const entities = await strapi.query('api::entity.entity').findMany({
      where: filters,
      populate: ['relations']
    });

    // Transform to CSV
    const csv = this.convertToCSV(entities);

    return {
      json: entities,
      csv: csv
    };
  },

  // Convert to CSV
  convertToCSV(data) {
    if (data.length === 0) return '';

    const headers = Object.keys(data[0]);
    const csvRows = [headers.join(',')];

    for (const row of data) {
      const values = headers.map(header => {
        const value = row[header];
        if (value === null || value === undefined) return '';
        if (typeof value === 'object') return JSON.stringify(value);
        return `"${String(value).replace(/"/g, '\"')}"`;
      });
      csvRows.push(values.join(','));
    }

    return csvRows.join('\n');
  }
});

// File: plugins/webhooks/server/register.js

module.exports = {
  register({ strapi }) {
    // Register webhook events
    strapi webhook.addEvent('entry.create');
    strapi webhook.addEvent('entry.update');
    strapi.websocket.addEvent('entry.delete');
  },

  async bootstrap({ strapi }) {
    // Bootstrap logic
    console.log('Webhook plugin loaded');
  },

  async destroy({ strapi }) {
    // Cleanup logic
    console.log('Webhook plugin destroyed');
  }
};

// File: plugins/webhooks/server/webhooks.js

module.exports = {
  'entry.create': async (event) => {
    const { model, uid, entry } = event;

    // Send webhook notification
    await strapi.plugin('webhooks').service('webhook').trigger({
      event: 'entry.create',
      data: {
        model,
        uid,
        entry,
        timestamp: new Date().toISOString()
      }
    });

    // Log event
    strapi.log.info(`Webhook triggered for ${model} creation: ${entry.id}`);
  },

  'entry.update': async (event) => {
    const { model, uid, entry, changes } = event;

    // Only send webhook if there are actual changes
    if (changes && Object.keys(changes).length > 0) {
      await strapi.plugin('webhooks').service('webhook').trigger({
        event: 'entry.update',
        data: {
          model,
          uid,
          entry,
          changes,
          timestamp: new Date().toISOString()
        }
      });
    }
  },

  'entry.delete': async (event) => {
    const { model, uid, entry } = event;

    await strapi.plugin('webhooks').service('webhook').trigger({
      event: 'entry.delete',
      data: {
        model,
        uid,
        entry,
        timestamp: new Date().toISOString()
      }
    });
  }
};

// File: plugins/analytics/server/services/analytics.js

'use strict';

module.exports = ({ strapi }) => ({
  // Track page view
  async trackPageView(data) {
    const { url, referrer, userAgent, ip } = data;

    const pageView = await strapi.query('api::page-view.page-view').create({
      data: {
        url,
        referrer,
        userAgent,
        ip,
        timestamp: new Date(),
        date: new Date().toISOString().split('T')[0]
      }
    });

    // Update daily stats
    await this.updateDailyStats('pageViews', pageView.date);

    return pageView;
  },

  // Track event
  async trackEvent(data) {
    const { type, category, action, label, value, url, userAgent, ip } = data;

    const event = await strapi.query('api::analytics-event.analytics-event').create({
      data: {
        type,
        category,
        action,
        label,
        value,
        url,
        userAgent,
        ip,
        timestamp: new Date(),
        date: new Date().toISOString().split('T')[0]
      }
    });

    // Update daily stats
    await this.updateDailyStats('events', event.date);

    return event;
  },

  // Update daily statistics
  async updateDailyStats(type, date) {
    const existingStat = await strapi.query('api::daily-stat.daily-stat').findOne({
      where: { date, type }
    });

    if (existingStat) {
      await strapi.query('api::daily-stat.daily-stat').update({
        where: { id: existingStat.id },
        data: {
          count: existingStat.count + 1,
          updatedAt: new Date()
        }
      });
    } else {
      await strapi.query('api::daily-stat.daily-stat').create({
        data: {
          date,
          type,
          count: 1
        }
      });
    }
  },

  // Get analytics data
  async getAnalytics(filters = {}) {
    const { startDate, endDate, type } = filters;

    const whereClause = {};
    if (startDate && endDate) {
      whereClause.date = {
        $gte: startDate,
        $lte: endDate
      };
    }
    if (type) {
      whereClause.type = type;
    }

    const stats = await strapi.query('api::daily-stat.daily-stat').findMany({
      where: whereClause,
      orderBy: { date: 'asc' }
    });

    return stats;
  },

  // Get popular content
  async getPopularContent(limit = 10) {
    const pageViews = await strapi.query('api::page-view.page-view').findMany({
      where: {
        date: {
          $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days
        }
      }
    });

    // Group by URL and count
    const urlCounts = pageViews.reduce((acc, view) => {
      acc[view.url] = (acc[view.url] || 0) + 1;
      return acc;
    }, {});

    // Sort by count and limit
    const popularUrls = Object.entries(urlCounts)
      .sort(([,a], [,b]) => b - a)
      .slice(0, limit)
      .map(([url, count]) => ({ url, count }));

    return popularUrls;
  }
});

// File: config/plugins.js - Plugin Configuration

module.exports = {
  // Enable plugins
  'users-permissions': {
    enabled: true,
    config: {
      jwtSecret: process.env.JWT_SECRET || 'default-secret',
      register: {
        allowedFields: ['username', 'email', 'password'],
        uniqueFields: ['email', 'username'],
      },
      confirmEmail: false,
    },
  },

  // Upload configuration
  upload: {
    enabled: true,
    config: {
      provider: 'local', // or 'aws-s3'
      providerOptions: {
        local: {
          path: './public/uploads'
        },
        awsS3: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
          region: process.env.AWS_REGION,
          bucket: process.env.AWS_S3_BUCKET,
        },
      },
    },
  },

  // Email configuration
  'email': {
    enabled: true,
    config: {
      provider: 'sendgrid', // or 'nodemailer'
      providerOptions: {
        sendgrid: {
          apiKey: process.env.SENDGRID_API_KEY,
        },
        nodemailer: {
          host: process.env.SMTP_HOST,
          port: process.env.SMTP_PORT,
          auth: {
            user: process.env.SMTP_USER,
            pass: process.env.SMTP_PASS,
          },
        },
      },
      settings: {
        defaultFrom: process.env.DEFAULT_FROM_EMAIL,
        defaultReplyTo: process.env.DEFAULT_REPLY_TO_EMAIL,
      },
    },
  },

  // i18n configuration
  i18n: {
    enabled: true,
    config: {
      defaultLocale: 'en',
      locales: ['en', 'es', 'fr', 'de', 'it', 'pt', 'zh'],
      fallbackLocale: 'en',
    },
  },

  // GraphQL configuration
  graphql: {
    enabled: true,
    config: {
      endpoint: '/graphql',
      shadowCRUD: true,
      playgroundAlways: process.env.NODE_ENV !== 'production',
      depthLimit: 7,
      apolloServer: {
        introspection: process.env.NODE_ENV !== 'production',
      },
    },
  },

  // Documentation configuration
  'documentation': {
    enabled: true,
    config: {
      openapi: {
        version: '1.0.0',
        info: {
          title: 'My API Documentation',
          description: 'Comprehensive API documentation',
          contact: {
            name: 'API Support',
            email: 'support@example.com',
          },
        },
        servers: [
          {
            url: 'http://localhost:1337/api',
            description: 'Development server',
          },
        ],
        security: [
          {
            bearerAuth: [],
          },
        ],
      },
    },
  },

  // Webhook configuration
  webhook: {
    enabled: true,
    config: {
      allowedOrigins: ['https://example.com', 'https://app.example.com'],
    },
  },

  // Redis cache configuration
  'redis': {
    enabled: true,
    config: {
      connections: {
        default: {
          host: process.env.REDIS_HOST || 'localhost',
          port: process.env.REDIS_PORT || 6379,
          password: process.env.REDIS_PASSWORD,
          db: 0,
        },
      },
    },
  },
};

// File: docker-compose.prod.yml - Production Deployment

version: '3.8'

services:
  # Strapi application
  strapi:
    build:
      context: .
      dockerfile: Dockerfile.prod
    image: strapi-app:latest
    restart: unless-stopped
    environment:
      NODE_ENV: production
      DATABASE_CLIENT: postgres
      DATABASE_HOST: postgres
      DATABASE_PORT: 5432
      DATABASE_NAME: ${DATABASE_NAME}
      DATABASE_USERNAME: ${DATABASE_USERNAME}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      JWT_SECRET: ${JWT_SECRET}
      APP_KEYS: ${APP_KEYS}
      API_TOKEN_SALT: ${API_TOKEN_SALT}
      ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
      TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT}
      REDIS_HOST: redis
      REDIS_PORT: 6379
      SENDGRID_API_KEY: ${SENDGRID_API_KEY}
      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
      AWS_S3_BUCKET: ${AWS_S3_BUCKET}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    ports:
      - "1337:1337"
    volumes:
      - ./public/uploads:/app/public/uploads
    networks:
      - strapi-network

  # PostgreSQL database
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DATABASE_NAME}
      POSTGRES_USER: ${DATABASE_USERNAME}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./config/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME} -d ${DATABASE_NAME}"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - strapi-network

  # Redis cache
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - strapi-network

  # Nginx reverse proxy
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - strapi
    networks:
      - strapi-network

  # Monitoring with Prometheus
  prometheus:
    image: prom/prometheus:latest
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    networks:
      - strapi-network

  # Grafana for visualization
  grafana:
    image: grafana/grafana:latest
    restart: unless-stopped
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana:/etc/grafana/provisioning:ro
    networks:
      - strapi-network

volumes:
  postgres_data:
  redis_data:
  prometheus_data:
  grafana_data:

networks:
  strapi-network:
    driver: bridge

# File: Dockerfile.prod - Production Dockerfile

FROM node:18-alpine AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY . .

# Build the app
RUN npm run build

# Production stage
FROM node:18-alpine AS production

# Install dumb-init for signal handling
RUN apk add --no-cache dumb-init

# Create app user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S strapi -u 1001

WORKDIR /app

# Copy built app
COPY --from=builder --chown=strapi:nodejs /app .

# Create uploads directory
RUN mkdir -p public/uploads && \
    chown -R strapi:nodejs public/uploads

# Switch to app user
USER strapi

# Expose port
EXPOSE 1337

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:1337/health || exit 1

# Start the app
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm", "start"]

# File: scripts/backup.sh - Backup Script

#!/bin/bash

# Configuration
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
STRAPI_CONTAINER="strapi_strapi_1"
DB_CONTAINER="strapi_postgres_1"

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup database
echo "Backing up database..."
docker exec $DB_CONTAINER pg_dump -U $DATABASE_USERNAME $DATABASE_NAME > $BACKUP_DIR/db_backup_$DATE.sql

# Backup uploads
echo "Backing up uploads..."
docker cp $STRAPI_CONTAINER:/app/public/uploads $BACKUP_DIR/uploads_$DATE

# Compress backups
echo "Compressing backups..."
tar -czf $BACKUP_DIR/strapi_backup_$DATE.tar.gz -C $BACKUP_DIR db_backup_$DATE.sql uploads_$DATE

# Clean up
rm -f $BACKUP_DIR/db_backup_$DATE.sql
rm -rf $BACKUP_DIR/uploads_$DATE

# Remove old backups (keep last 7 days)
find $BACKUP_DIR -name "strapi_backup_*.tar.gz" -mtime +7 -delete

echo "Backup completed: $BACKUP_DIR/strapi_backup_$DATE.tar.gz"

Werkzeuge

Tools, die oft mit diesem Beispiel genutzt werden

Verwandt

Verwandte Beispielsammlungen

Verwandt

Verwandte Themen

Thema

JSON Schema aus Beispielen und Tabellen ableiten

Leiten Sie aus repräsentativen JSON-Beispielen oder XLSX-Zeilen einen prüfbaren JSON-Schema-Entwurf ab und kontrollieren Sie Typen, Pflichtfelder, Nullwerte und Ausreißer, bevor er als Vertrag dient.

Thema

Selbst gehostetes Gewinnspiel-Widget prüfen und zur Einbettung übergeben

Konfiguration, API-Beispiele, Lesbarkeit und responsive Ansichten prüfen und die Widget-Dateien mit nachvollziehbaren Browser-Abnahmetests übergeben.

Thema

Standardisierung und Komprimierung von Bildern im Batch

Bereiten Sie einen einheitlichen Bildbestand vor: Ausrichtung korrigieren, unerwünschte Ränder entfernen, eine Größenregel anwenden, Dateien komprimieren und repräsentative Ausgaben vor der Übergabe prüfen.

Thema

Klassische und moderne Verschlüsselungswerkzeuge

Üben Sie sichere Lern- und CTF-Abläufe, indem Sie reversible klassische Verfahren mit moderner Verschlüsselung und ihren Grenzen vergleichen.

Start
Werkzeuge3212
Workbench
Beispiele593
Visualisierungen471
Dateibetrachter103
Themen239
Schlagwörter333
Bilder, Audio & Video657

Werkzeuge für Bild-, Audio- und Videobearbeitung, Konvertierung und Optimierung

Mathe, Datum & Finanzen647

Rechner, Zahlenwerkzeuge, Datumslogik, Statistik und Finanztools

Design & Farbe298

Werkzeuge für Farben, Layouts, Grafiken, visuelle Gestaltung und Designhilfen

Text & Schreiben209

Werkzeuge für Schreiben, Bereinigung, Formatierung, Extraktion und Textanalyse

Entwicklung & Web193

Entwicklerwerkzeuge, Netzwerk-Utilities, Web-Debugging und Automatisierungshelfer

Konvertierung & Kodierung175

Werkzeuge für Format-, Datei-, Archiv-, Einheiten- und Kodierungskonvertierung

Lead-Quiz veröffentlichen und Kontakte sicher übergeben
Gedenktexte: Familienprüfung und Übergabe zum Druck
Private Gewohnheiten: Sicherung, Wiederherstellung und Freigabeprüfung
Vom Artikel zu X- und LinkedIn-Entwürfen: Faktenprüfung und Übergabe
Interaktive Markdown-Lektion prüfen und offline übergeben

© 2026 Elysia Tools. Alle Rechte vorbehalten.

DokumentationTarifeÜber unsNutzungsbedingungenDatenschutzerklärungCookiesKontakt
CMS

Contentful Cloud-basierte CMS Beispiele

Umfassende Contentful Beispiele mit Content Modeling, API Integration, Webhooks und Frontend Integration Patterns

CMS

Sanity Echtzeit-CMS Beispiele

Umfassende Sanity Beispiele für Echtzeit-Inhaltsverwaltung, API-Integration und Bereitstellungsmuster