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
/**
* 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.
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.
// 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).
// Strapi v5
const article = await articleService.findOne('abc123def456', {
populate: ['author', 'categories', 'coverImage'],
});
if (article) {
console.log(article.title);
}
// 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).const article = await articleService.findBySlug('my-first-post', {
populate: '*',
});
Create a new entry.
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.
const updated = await articleService.update('abc123', {
status: 'published',
publishedAt: new Date().toISOString(),
});
Delete an entry.
await articleService.delete('abc123');
Count entries matching filters.
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
/**
* 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:const { data } = await articleService.findMany({
filters: {
status: 'published',
featured: true,
},
});
Sorting
Sort by one or multiple fields:// 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
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}`);
const { data, pagination } = await articleService.findMany({
pagination: {
start: 10, // Skip first 10 items
limit: 5, // Get 5 items
},
});
Population
Populate related fields:// Populate everything (use sparingly)
const article = await articleService.findOne('abc123', {
populate: '*',
});
Localization
For internationalized content types: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:// 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
---
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
---
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
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
// 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.
Next Steps
Schemas
Validate data with generated Zod schemas
Relations
Work with related content types
Media
Upload and manage files
Types
Understand generated TypeScript types