> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strapi2front.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Output Structure

> Understanding the by-feature file organization and generated code structure

strapi2front uses a **by-feature** ("screaming architecture") output structure. Each content type lives in its own folder with all related code co-located.

## Structure Overview

```typescript title="strapi.config.ts" theme={null}
output: {
  path: "src/strapi",  // Base output directory
}
```

Generated structure:

```
src/strapi/
├── collections/           # Collection types (many entries)
│   ├── article/
│   │   ├── types.ts      # TypeScript interfaces
│   │   ├── schemas.ts    # Zod validation schemas
│   │   ├── service.ts    # API service functions
│   │   └── actions.ts    # Astro Actions
│   ├── author/
│   │   ├── types.ts
│   │   ├── schemas.ts
│   │   └── service.ts
│   └── category/
│       └── ...
├── singles/               # Single types (one entry)
│   ├── homepage/
│   │   ├── types.ts
│   │   ├── schemas.ts
│   │   └── service.ts
│   └── settings/
│       └── ...
├── components/            # Reusable components
│   ├── hero.ts           # Component type + schema
│   ├── seo.ts
│   └── cta-button.ts
└── shared/                # Shared utilities
    ├── utils.ts          # System types (media, pagination, etc.)
    ├── client.ts         # Strapi client wrapper
    ├── locales.ts        # i18n locale definitions
    ├── upload-client.ts  # Browser upload helper (if enabled)
    └── upload-action.ts  # Server upload action (if enabled)
```

## Collections vs Singles

<Tabs>
  <Tab title="Collections">
    Content types that have **multiple entries** (articles, products, users, etc.)

    **Location:** `collections/{singular-name}/`

    **Files:**

    * `types.ts` - Type definitions + filters
    * `schemas.ts` - Create/update validation
    * `service.ts` - Full CRUD operations
    * `actions.ts` - Astro action wrappers

    **Example:**

    ```
    collections/
      article/
        types.ts      → Article, ArticleFilters
        schemas.ts    → articleCreateSchema, articleUpdateSchema
        service.ts    → articleService (findMany, findOne, create, etc.)
        actions.ts    → article actions
    ```
  </Tab>

  <Tab title="Singles">
    Content types that have **one entry** (homepage, settings, about page, etc.)

    **Location:** `singles/{singular-name}/`

    **Files:**

    * `types.ts` - Type definition only (no filters)
    * `schemas.ts` - Update validation only
    * `service.ts` - find/update/delete operations

    **Example:**

    ```
    singles/
      homepage/
        types.ts      → Homepage
        schemas.ts    → homepageUpdateSchema
        service.ts    → homepageService (find, update, delete)
    ```

    <Info>
      Single types don't have `create()` or `findMany()` - there's only one instance.
    </Info>
  </Tab>
</Tabs>

## File Naming

All folder and file names use **kebab-case** derived from your Strapi content type names:

| Strapi Content Type     | Folder Name         | Import Path                     |
| ----------------------- | ------------------- | ------------------------------- |
| `Article`               | `article/`          | `collections/article/`          |
| `BlogPost`              | `blog-post/`        | `collections/blog-post/`        |
| `ProductCategory`       | `product-category/` | `collections/product-category/` |
| `SEO` (component)       | `seo.ts`            | `components/seo`                |
| `CTAButton` (component) | `cta-button.ts`     | `components/cta-button`         |

<Note>
  The converter uses `singularName` from Strapi (e.g., "article", "blogPost") and converts to kebab-case.
</Note>

## Import Patterns

strapi2front does **NOT** generate barrel exports (`index.ts`). Always import from specific files:

<CodeGroup>
  ```typescript title="✅ Correct - Import from specific file" theme={null}
  import { articleService } from '@/strapi/collections/article/service';
  import type { Article } from '@/strapi/collections/article/types';
  import { articleCreateSchema } from '@/strapi/collections/article/schemas';
  ```

  ```typescript title="❌ Incorrect - No barrel exports" theme={null}
  // These won't work:
  import { articleService } from '@/strapi/collections/article';
  import { Article } from '@/strapi/collections';
  ```
</CodeGroup>

### Multiple Imports from Same Feature

```typescript theme={null}
// Import multiple items from same file
import type { 
  Article, 
  ArticleFilters,
  ArticleCreateInput,
  ArticleUpdateInput 
} from '@/strapi/collections/article/types';

// Import from different files
import { articleService } from '@/strapi/collections/article/service';
import { articleCreateSchema } from '@/strapi/collections/article/schemas';
import type { Article } from '@/strapi/collections/article/types';
```

## Generated File Contents

### Types File

<Accordion title="collections/{name}/types.ts">
  ```typescript theme={null}
  /**
   * Article
   * News and blog articles
   * Generated by strapi2front
   */

  import type { StrapiBaseEntity, StrapiMedia, BlocksContent } from '../../shared/utils';
  import type { Author } from '../../collections/author/types';
  import type { Tag } from '../tag/types';
  import type { Seo } from '../../components/seo';

  export interface Article extends StrapiBaseEntity {
    title: string;
    slug: string;
    content: BlocksContent;
    excerpt?: string;
    cover: StrapiMedia | null;
    author: Author | null;
    tags: Tag[];
    seo: Seo | null;
    publishedDate: string;
  }

  export interface ArticleFilters {
    id?: number | { $eq?: number; $ne?: number; $in?: number[]; $notIn?: number[] };
    documentId?: string | { $eq?: string; $ne?: string };
    title?: string | { $contains?: string; $startsWith?: string; $endsWith?: string };
    slug?: string | { $eq?: string };
    createdAt?: string | { $gt?: string; $gte?: string; $lt?: string; $lte?: string };
    updatedAt?: string | { $gt?: string; $gte?: string; $lt?: string; $lte?: string };
    publishedAt?: string | null | { $eq?: string; $ne?: string; $null?: boolean };
    $and?: ArticleFilters[];
    $or?: ArticleFilters[];
    $not?: ArticleFilters;
  }
  ```

  **Includes:**

  * Interface extending `StrapiBaseEntity`
  * Relations imported from other types
  * Filter interface for querying (collections only)
</Accordion>

<Accordion title="collections/{name}/schemas.ts">
  ```typescript theme={null}
  /**
   * Article Schemas
   * Generated by strapi2front
   */

  import { z } from 'zod';
  import { seoSchema } from '../../components/seo';

  /**
   * Zod schema for creating articles
   */
  export const articleCreateSchema = z.object({
    title: z.string(),
    slug: z.string(),
    content: z.array(z.any()),  // BlocksContent
    excerpt: z.string().optional(),
    cover: z.number().optional(),
    author: z.string().optional(),  // documentId
    tags: z.array(z.string()).optional(),
    seo: seoSchema.optional(),
    publishedDate: z.string().optional(),
  });

  /**
   * Zod schema for updating articles (all fields optional)
   */
  export const articleUpdateSchema = z.object({
    title: z.string().optional(),
    slug: z.string().optional(),
    content: z.array(z.any()).optional(),
    excerpt: z.string().optional(),
    cover: z.number().optional(),
    author: z.string().optional(),
    tags: z.array(z.string()).optional(),
    seo: seoSchema.optional(),
    publishedDate: z.string().optional(),
  });

  export type ArticleCreateInput = z.infer<typeof articleCreateSchema>;
  export type ArticleUpdateInput = z.infer<typeof articleUpdateSchema>;
  ```

  **Includes:**

  * Create schema (required fields enforced)
  * Update schema (all fields optional)
  * TypeScript types inferred from schemas
</Accordion>

<Accordion title="collections/{name}/service.ts">
  See [Features > Services](/configuration/features#services) for complete service API.

  **Exports:**

  * `{name}Service` object with methods:
    * `findMany()` - Paginated list
    * `findAll()` - All entries (auto-paginated)
    * `findOne()` - By documentId/id
    * `findBySlug()` - By slug (if exists)
    * `create()` - Create new
    * `update()` - Update existing
    * `delete()` - Delete
    * `count()` - Count with filters
</Accordion>

<Accordion title="collections/{name}/actions.ts">
  ```typescript theme={null}
  /**
   * Article Actions (Astro)
   * Generated by strapi2front
   */

  import { defineAction } from 'astro:actions';
  import { z } from 'zod';
  import { articleService } from './service';
  import { articleCreateSchema, articleUpdateSchema } from './schemas';

  const findManySchema = z.object({
    filters: z.any().optional(),
    pagination: z.object({
      page: z.number().optional(),
      pageSize: z.number().optional(),
    }).optional(),
    populate: z.any().optional(),
  });

  export const article = {
    findMany: defineAction({
      input: findManySchema,
      handler: async (input) => {
        return articleService.findMany(input);
      },
    }),
    
    findOne: defineAction({
      input: z.object({ documentId: z.string() }),
      handler: async ({ documentId }) => {
        return articleService.findOne(documentId);
      },
    }),
    
    create: defineAction({
      input: articleCreateSchema,
      handler: async (input) => {
        return articleService.create(input);
      },
    }),
    
    // ... update, delete actions
  };
  ```
</Accordion>

### Components

Components generate a single file with type + schema:

<Accordion title="components/{name}.ts">
  ```typescript theme={null}
  /**
   * SEO component
   * Category: shared
   * Generated by strapi2front
   */

  import { z } from 'zod';
  import type { StrapiMedia } from '../shared/utils';

  export interface Seo {
    id: number;
    metaTitle: string;
    metaDescription: string;
    metaImage: StrapiMedia | null;
    keywords?: string;
  }

  /**
   * Zod schema for SEO component
   */
  export const seoSchema = z.object({
    metaTitle: z.string(),
    metaDescription: z.string(),
    metaImage: z.number().optional(),
    keywords: z.string().optional(),
  });

  export type SeoInput = z.infer<typeof seoSchema>;
  ```
</Accordion>

### Shared Files

<Accordion title="shared/utils.ts">
  System types used across all generated code:

  ```typescript theme={null}
  /**
   * Strapi utility types
   * Generated by strapi2front
   * Strapi version: v5
   */

  export interface StrapiBaseEntity {
    id: number;
    documentId: string;  // v5 only
    createdAt: string;
    updatedAt: string;
    publishedAt: string | null;
  }

  export interface StrapiMedia {
    id: number;
    documentId: string;
    name: string;
    alternativeText: string | null;
    caption: string | null;
    width: number;
    height: number;
    url: string;
    formats: { /* ... */ } | null;
    // ...
  }

  export interface StrapiPagination {
    page: number;
    pageSize: number;
    pageCount: number;
    total: number;
  }

  export interface StrapiResponse<T> { /* ... */ }
  export interface StrapiListResponse<T> { /* ... */ }
  export interface StrapiFileInfo { /* ... */ }

  // Rich text content
  export type BlocksContent = unknown[];  // or from @strapi/blocks-react-renderer
  ```
</Accordion>

<Accordion title="shared/client.ts">
  Wrapper around `@strapi/client` with type safety:

  ```typescript theme={null}
  import { strapi } from '@strapi/client';

  export interface ClientOptions {
    authToken?: string;
    baseURL?: string;
  }

  export function createStrapiClient(options?: ClientOptions) { /* ... */ }
  export const strapiClient = createStrapiClient();

  export function collection<T>(pluralName: string, clientOptions?: ClientOptions) { /* ... */ }
  export function single<T>(singularName: string, clientOptions?: ClientOptions) { /* ... */ }

  export const files = {
    upload: async (file: File, options?) => { /* ... */ },
    find: async (params?) => { /* ... */ },
    // ...
  };
  ```
</Accordion>

<Accordion title="shared/locales.ts">
  i18n locale definitions from your Strapi instance:

  ```typescript theme={null}
  /**
   * Strapi locales
   * Generated by strapi2front
   */

  export const locales = ['en', 'es', 'fr'] as const;
  export type Locale = typeof locales[number];

  export const defaultLocale: Locale = 'en';

  export const localeNames: Record<Locale, string> = {
    'en': 'English',
    'es': 'Español',
    'fr': 'Français',
  };

  export function isValidLocale(code: string): code is Locale {
    return locales.includes(code as Locale);
  }

  export function getLocaleName(code: Locale): string {
    return localeNames[code] || code;
  }
  ```
</Accordion>

## Path Alias Setup

For cleaner imports, configure a path alias in your project:

<Tabs>
  <Tab title="TypeScript">
    ```json title="tsconfig.json" theme={null}
    {
      "compilerOptions": {
        "baseUrl": ".",
        "paths": {
          "@/strapi/*": ["./src/strapi/*"]
        }
      }
    }
    ```

    Usage:

    ```typescript theme={null}
    import { articleService } from '@/strapi/collections/article/service';
    ```
  </Tab>

  <Tab title="Astro">
    ```typescript title="astro.config.mjs" theme={null}
    export default defineConfig({
      vite: {
        resolve: {
          alias: {
            '@/strapi': '/src/strapi',
          },
        },
      },
    });
    ```
  </Tab>

  <Tab title="Next.js">
    ```json title="tsconfig.json" theme={null}
    {
      "compilerOptions": {
        "paths": {
          "@/strapi/*": ["./src/strapi/*"]
        }
      }
    }
    ```

    Next.js automatically supports `tsconfig.json` paths.
  </Tab>

  <Tab title="Vite">
    ```typescript title="vite.config.ts" theme={null}
    import { defineConfig } from 'vite';
    import path from 'path';

    export default defineConfig({
      resolve: {
        alias: {
          '@/strapi': path.resolve(__dirname, './src/strapi'),
        },
      },
    });
    ```
  </Tab>
</Tabs>

## Why By-Feature?

<AccordionGroup>
  <Accordion title="Scalability">
    Projects can grow to hundreds of content types without becoming unwieldy. Each content type is isolated in its own folder.

    ```
    collections/
      article/     ← 4 files
      author/      ← 4 files
      category/    ← 4 files
      ...          ← 100+ more, easy to navigate
    ```
  </Accordion>

  <Accordion title="Encapsulation">
    Everything related to a content type lives together. Want to see all article-related code? Look in `collections/article/`.

    No hunting across `types/`, `services/`, `schemas/` folders.
  </Accordion>

  <Accordion title="Clear Dependencies">
    Import paths clearly show relationships:

    ```typescript theme={null}
    // Article depends on Author and Tag
    import type { Author } from '../../collections/author/types';
    import type { Tag } from '../tag/types';
    ```
  </Accordion>

  <Accordion title="Easy Refactoring">
    Renaming or removing a content type? Just delete/rename its folder. All related code is co-located.
  </Accordion>
</AccordionGroup>

## Alternative Structures

strapi2front currently only supports by-feature structure. Future versions may add:

* **By-type structure** - Group by file type (`types/`, `services/`, etc.)
* **Flat structure** - All files in one directory
* **Custom templates** - Define your own structure

<Info>
  Interested in other structures? [Open an issue](https://github.com/Eleven-Estudio/strapi2front/issues) to discuss!
</Info>

## Output Example

With this Strapi schema:

* Collections: Article, Author, Category
* Singles: Homepage
* Components: SEO, Hero

You get:

```
src/strapi/
├── collections/
│   ├── article/
│   │   ├── types.ts      (Article, ArticleFilters)
│   │   ├── schemas.ts    (articleCreateSchema, articleUpdateSchema)
│   │   ├── service.ts    (articleService)
│   │   └── actions.ts    (article actions)
│   ├── author/
│   │   ├── types.ts
│   │   ├── schemas.ts
│   │   ├── service.ts
│   │   └── actions.ts
│   └── category/
│       └── ...
├── singles/
│   └── homepage/
│       ├── types.ts      (Homepage)
│       ├── schemas.ts    (homepageUpdateSchema)
│       └── service.ts    (homepageService)
├── components/
│   ├── seo.ts            (Seo, seoSchema)
│   └── hero.ts           (Hero, heroSchema)
└── shared/
    ├── utils.ts
    ├── client.ts
    ├── locales.ts
    └── upload-client.ts  (if enabled)
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Using Services" icon="code" href="/guides/services">
    Learn how to use generated services
  </Card>

  <Card title="Type Safety" icon="shield-check" href="/guides/types">
    Understand the type system
  </Card>
</CardGroup>
