What Gets Generated
For each content type, you get a service module with methods for:Collection Type Services
Generated Service Example
For an Article collection type, strapi2front generates:article.service.ts
Copy
Ask AI
/**
* Article Service
* Blog article with author and categories
* Generated by strapi2front
* Strapi version: v5
*/
import { collection } from '../client';
import type { Article, ArticleFilters } from '@/types/collections/article';
import type { StrapiPagination } from '@/types/utils';
import type { Locale } from '../locales';
export interface FindManyOptions {
filters?: ArticleFilters;
pagination?: {
/** Page number (1-indexed) - use with pageSize */
page?: number;
/** Number of items per page (default: 25) - use with page */
pageSize?: number;
/** Offset to start from (0-indexed) - use with limit */
start?: number;
/** Maximum number of items to return - use with start */
limit?: number;
};
sort?: string | string[];
populate?: string | string[] | Record<string, unknown>;
locale?: Locale; // Only if localized
status?: 'draft' | 'published'; // Only if draft & publish enabled
}
export interface FindOneOptions {
populate?: string | string[] | Record<string, unknown>;
locale?: Locale;
status?: 'draft' | 'published';
}
// Create typed collection helper
const articleCollection = collection<Article>('articles');
export const articleService = {
/**
* Find multiple articles
*/
async findMany(options: FindManyOptions = {}): Promise<{ data: Article[]; pagination: StrapiPagination }> {
const response = await articleCollection.find({
filters: options.filters,
pagination: options.pagination,
sort: options.sort,
populate: options.populate,
locale: options.locale,
status: options.status,
});
return {
data: response.data,
pagination: response.meta.pagination,
};
},
/**
* Find all articles (handles pagination automatically)
*/
async findAll(options: Omit<FindManyOptions, 'pagination'> = {}): Promise<Article[]> {
const allItems: Article[] = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const { data, pagination } = await this.findMany({
...options,
pagination: { page, pageSize: 100 },
});
allItems.push(...data);
hasMore = page < pagination.pageCount;
page++;
}
return allItems;
},
/**
* Find one article by documentId
*/
async findOne(documentId: string, options: FindOneOptions = {}): Promise<Article | null> {
try {
const response = await articleCollection.findOne(documentId, {
populate: options.populate,
locale: options.locale,
status: options.status,
});
return response.data;
} catch (error) {
// Return null if not found
if (error instanceof Error && error.message.includes('404')) {
return null;
}
throw error;
}
},
/**
* Find one article by slug
*/
async findBySlug(slug: string, options: FindOneOptions = {}): Promise<Article | null> {
const { data } = await this.findMany({
filters: { slug: { $eq: slug } } as ArticleFilters,
pagination: { pageSize: 1 },
populate: options.populate,
locale: options.locale,
status: options.status,
});
return data[0] || null;
},
/**
* Create a new article
*/
async create(data: Partial<Omit<Article, 'id' | 'documentId' | 'createdAt' | 'updatedAt' | 'publishedAt'>>): Promise<Article> {
const response = await articleCollection.create({ data });
return response.data;
},
/**
* Update an article
*/
async update(documentId: string, data: Partial<Omit<Article, 'id' | 'documentId' | 'createdAt' | 'updatedAt' | 'publishedAt'>>): Promise<Article> {
const response = await articleCollection.update(documentId, { data });
return response.data;
},
/**
* Delete an article
*/
async delete(documentId: string): Promise<void> {
await articleCollection.delete(documentId);
},
/**
* Count articles
*/
async count(filters?: ArticleFilters): Promise<number> {
const { pagination } = await this.findMany({
filters,
pagination: { pageSize: 1 },
});
return pagination.total;
},
};
Service Methods
- findMany
- findAll
- findOne
- findBySlug
- create
- update
- delete
- count
Query multiple items with filtering, sorting, and pagination.
Copy
Ask AI
const { data, pagination } = await articleService.findMany({
filters: {
status: 'published',
publishedAt: { $gte: '2024-01-01' },
},
sort: ['-publishedAt', 'title'],
pagination: { page: 1, pageSize: 10 },
populate: ['author', 'categories', 'coverImage'],
});
console.log(`Found ${data.length} articles`);
console.log(`Total: ${pagination.total}, Pages: ${pagination.pageCount}`);
Fetch all items automatically handling pagination.
Use with caution on large datasets. This method fetches ALL items.
Copy
Ask AI
// Fetches all published articles, paginating automatically
const allArticles = await articleService.findAll({
filters: { status: 'published' },
sort: '-publishedAt',
populate: ['author'],
});
console.log(`Retrieved ${allArticles.length} total articles`);
Find a single item by its documentId (v5) or id (v4).
Copy
Ask AI
// Strapi v5
const article = await articleService.findOne('abc123def456', {
populate: ['author', 'categories', 'coverImage'],
});
if (article) {
console.log(article.title);
}
Copy
Ask AI
// Strapi v4
const article = await articleService.findOne(42, {
populate: ['author', 'categories'],
});
Find by slug field (only generated if your content type has a
slug attribute).Copy
Ask AI
const article = await articleService.findBySlug('my-first-post', {
populate: '*',
});
Create a new entry.
Copy
Ask AI
const newArticle = await articleService.create({
title: 'New Article',
slug: 'new-article',
content: [
{
type: 'paragraph',
children: [{ type: 'text', text: 'Hello world' }],
},
],
status: 'draft',
});
console.log(`Created article with ID: ${newArticle.documentId}`);
Update an existing entry.
Copy
Ask AI
const updated = await articleService.update('abc123', {
status: 'published',
publishedAt: new Date().toISOString(),
});
Delete an entry.
Copy
Ask AI
await articleService.delete('abc123');
Count entries matching filters.
Copy
Ask AI
const publishedCount = await articleService.count({
status: 'published',
});
console.log(`${publishedCount} published articles`);
Single Type Services
For single types (like Homepage, Settings), the service is simpler:homepage.service.ts
Copy
Ask AI
/**
* Homepage Service (Single Type)
* Main homepage configuration
* Generated by strapi2front
* Strapi version: v5
*/
import { single } from '../client';
import type { Homepage } from '@/types/collections/homepage';
import type { Locale } from '../locales';
export interface FindOptions {
populate?: string | string[] | Record<string, unknown>;
locale?: Locale;
status?: 'draft' | 'published';
}
const homepageSingle = single<Homepage>('homepage');
export const homepageService = {
/**
* Get Homepage
*/
async find(options: FindOptions = {}): Promise<Homepage | null> {
try {
const response = await homepageSingle.find({
populate: options.populate,
locale: options.locale,
status: options.status,
});
return response.data;
} catch (error) {
if (error instanceof Error && error.message.includes('404')) {
return null;
}
throw error;
}
},
/**
* Update Homepage
*/
async update(data: Partial<Omit<Homepage, 'id' | 'documentId' | 'createdAt' | 'updatedAt'>>): Promise<Homepage> {
const response = await homepageSingle.update({ data });
return response.data;
},
/**
* Delete Homepage
*/
async delete(): Promise<void> {
await homepageSingle.delete();
},
};
Query Options
Filtering
Use typed filters with Strapi’s query operators:Copy
Ask AI
const { data } = await articleService.findMany({
filters: {
status: 'published',
featured: true,
},
});
Sorting
Sort by one or multiple fields:Copy
Ask AI
// Single field
await articleService.findMany({ sort: 'title' });
// Multiple fields
await articleService.findMany({ sort: ['-publishedAt', 'title'] });
// Descending order (prefix with -)
await articleService.findMany({ sort: '-views' });
Pagination
Two pagination styles are supported:- Page-based
- Offset-based
Copy
Ask AI
const { data, pagination } = await articleService.findMany({
pagination: {
page: 2, // Page number (1-indexed)
pageSize: 20, // Items per page
},
});
console.log(`Page ${pagination.page} of ${pagination.pageCount}`);
Copy
Ask AI
const { data, pagination } = await articleService.findMany({
pagination: {
start: 10, // Skip first 10 items
limit: 5, // Get 5 items
},
});
Population
Populate related fields:Copy
Ask AI
// Populate everything (use sparingly)
const article = await articleService.findOne('abc123', {
populate: '*',
});
Localization
For internationalized content types:Copy
Ask AI
import type { Locale } from '@/strapi/locales';
// Fetch French version
const article = await articleService.findOne('abc123', {
locale: 'fr',
});
// Fetch all French articles
const { data } = await articleService.findMany({
locale: 'fr',
filters: { status: 'published' },
});
Draft & Publish
For content types with draft & publish enabled:Copy
Ask AI
// Get published version (default)
const article = await articleService.findOne('abc123', {
status: 'published',
});
// Get draft version
const draft = await articleService.findOne('abc123', {
status: 'draft',
});
Real-World Examples
Blog Post Page
pages/blog/[slug].astro
Copy
Ask AI
---
import { articleService } from '@/strapi/collections/article/service';
import type { Article } from '@/strapi/collections/article/types';
const { slug } = Astro.params;
const article = await articleService.findBySlug(slug, {
populate: {
author: { populate: ['avatar'] },
categories: true,
coverImage: true,
seo: true,
},
status: 'published',
});
if (!article) {
return Astro.redirect('/404');
}
---
<article>
<h1>{article.title}</h1>
{article.coverImage && (
<img src={article.coverImage.url} alt={article.coverImage.alternativeText} />
)}
<div class="meta">
<span>By {article.author?.name}</span>
<time>{new Date(article.publishedAt).toLocaleDateString()}</time>
</div>
<div class="content">
<BlocksRenderer content={article.content} />
</div>
</article>
Paginated Blog List
pages/blog/page/[page].astro
Copy
Ask AI
---
import { articleService } from '@/strapi/collections/article/service';
const currentPage = Number(Astro.params.page) || 1;
const pageSize = 12;
const { data: articles, pagination } = await articleService.findMany({
filters: { status: 'published' },
sort: ['-publishedAt'],
pagination: { page: currentPage, pageSize },
populate: ['author', 'coverImage', 'categories'],
});
---
<div class="articles">
{articles.map(article => (
<ArticleCard article={article} />
))}
</div>
<Pagination
currentPage={pagination.page}
totalPages={pagination.pageCount}
basePath="/blog/page"
/>
Search Results
api/search.ts
Copy
Ask AI
import { articleService } from '@/strapi/collections/article/service';
export async function GET({ url }: { url: URL }) {
const query = url.searchParams.get('q');
if (!query) {
return new Response(JSON.stringify({ results: [] }), {
headers: { 'Content-Type': 'application/json' },
});
}
const { data: results } = await articleService.findMany({
filters: {
$or: [
{ title: { $contains: query } },
{ excerpt: { $contains: query } },
],
status: 'published',
},
pagination: { pageSize: 20 },
populate: ['coverImage'],
});
return new Response(JSON.stringify({ results }), {
headers: { 'Content-Type': 'application/json' },
});
}
Strapi v4 Differences
For Strapi v4, the main differences are:- Uses
id: numberinstead ofdocumentId: string - No
documentIdfield in responses - Services accept
id: numberparameter instead ofdocumentId: string
Copy
Ask AI
// Strapi v4
const article = await articleService.findOne(42); // number ID
// Strapi v5
const article = await articleService.findOne('abc123def456'); // documentId string
strapi2front automatically generates the correct service based on your Strapi version.