Skip to Content
CLI ReferenceCode Generation

Code Generation

The foir pull command generates TypeScript types, GraphQL documents, typed operations, and optionally React hooks, Remix loaders, or Swift types from your platform models. This gives your frontend code type-safe access to your content schema.

Basic Usage

foir pull

This fetches all models from the platform and generates output into your project. By default, files are written to ./src/generated/.

Command Options

foir pull [options]
OptionDescription
--config <path>Path to config file (default: auto-discovers from project root)
--only <models>Comma-separated model keys to generate (e.g., page,blog-post)
--no-prettierSkip Prettier formatting on generated files
--dry-runShow what would be generated without writing any files
--out <dir>Override the output directory for types
--swift <dir>Generate Swift type files to the specified directory

Examples

# Generate for all models foir pull # Generate only specific models foir pull --only page,blog-post # Preview without writing files foir pull --dry-run # Override output directory foir pull --out ./src/types # Generate Swift types alongside TypeScript foir pull --swift ./ios/Generated

Configuration File

Create a foir.config.ts in your project root for persistent configuration. The CLI searches for config files in this order: foir.config.ts, foir.config.js, foir.config.mjs, .foirrc.ts, .foirrc.js, .foirrc.mjs.

import { defineConfig } from '@eide/foir-cli/config'; export default defineConfig({ pull: { output: { types: './src/generated/types', documents: './src/generated/documents', operations: './src/generated/operations', hooks: './src/generated/hooks', loaders: './src/generated/loaders', swift: './ios/Generated', }, targets: ['react'], only: ['blog-post', 'page'], domains: true, includeInline: true, prettier: true, }, });

Configuration Options

output

Controls where generated files are written.

PropertyDefaultDescription
types./src/generated/typesDirectory for TypeScript type files
documents./src/generated/documentsDirectory for GraphQL document files
operationsSibling of typesDirectory for typed operation modules
hooksSibling of typesDirectory for React hooks (requires 'react' target)
loadersSibling of typesDirectory for Remix loaders (requires 'remix' target)
swiftDirectory for Swift type files (omit to skip Swift generation)

targets

An array of code generation targets. Each target enables additional output:

  • 'react' — Generates Apollo Client hooks (useQuery, useMutation) for each model. Output is written to the hooks directory.
  • 'remix' — Generates typed loader functions for Remix routes. Output is written to the loaders directory.
export default defineConfig({ pull: { targets: ['react', 'remix'], }, });

domains

Controls which platform domain documents are generated. Set to true to generate all domains (the default), false to skip all domain documents, or provide a selective object:

export default defineConfig({ pull: { domains: { auth: true, authProviders: true, files: true, sync: true, notifications: true, operations: true, schedules: true, sharing: true, search: true, analytics: true, }, }, });

only

Filter code generation to specific model keys. When omitted or empty, all models are generated.

export default defineConfig({ pull: { only: ['blog-post', 'page', 'author'], }, });

This can also be set from the command line with --only blog-post,page,author. CLI flags override the config file.

includeInline

When true (the default), inline-only models are included for type resolution. These are models that are embedded within other models rather than stored as independent records. Including them ensures type completeness.

prettier

When true (the default), generated files are formatted with Prettier using your project’s Prettier configuration. Pass --no-prettier on the command line to skip formatting (useful in CI for faster builds).

Generated Output Structure

After running foir pull, your generated directory will contain:

src/generated/ ├── types/ │ ├── index.ts # Re-exports all types │ ├── field-types.ts # Shared value types (ImageValue, LinkValue, etc.) │ ├── config.ts # ModelConfig interface and defineModel helper │ ├── customer-profile.ts # Customer profile types (if schema exists) │ └── models/ │ ├── index.ts # Re-exports all model types │ ├── blog-post.ts # BlogPostData type + blogPostConfig │ └── page.ts # PageData type + pageConfig ├── documents/ │ ├── blog-post.graphql # GetBlogPost, ListBlogPosts, Create/Update/Delete │ ├── page.graphql # GetPage, ListPages, Create/Update/Delete │ ├── customer-profile.graphql # Customer profile operations │ ├── _shared.graphql # Shared fragments (if sharing is enabled) │ └── public-schema.graphql # Full public GraphQL schema ├── operations/ │ ├── _common.ts # Shared operation types │ ├── index.ts # Re-exports all operations │ ├── blog-post.ts # Typed operations for blog posts │ ├── page.ts # Typed operations for pages │ └── customer-profile.ts # Customer profile operations ├── hooks/ # (when target includes 'react') │ ├── index.ts # Re-exports all hooks │ ├── blog-post.ts # useBlogPost, useBlogPosts, etc. │ ├── page.ts # usePage, usePages, etc. │ └── customer-profile.ts # useCustomerProfile, etc. └── loaders/ # (when target includes 'remix') ├── index.ts # Re-exports all loaders ├── blog-post.ts # loadBlogPost, loadBlogPosts, etc. ├── page.ts # loadPage, loadPages, etc. └── customer-profile.ts # loadCustomerProfile, etc.

When Swift generation is enabled (--swift or output.swift), an additional directory is created:

ios/Generated/ ├── FieldTypes.swift # Shared Swift value types ├── ModelKeys.swift # Enum of all model keys ├── BlogPost.swift # BlogPost struct ├── Page.swift # Page struct └── CustomerProfile.swift # CustomerProfile struct (if schema exists)

Using Generated Types

TypeScript Types

import type { BlogPostData, PageData } from './generated/types'; import { blogPostConfig } from './generated/types'; // Type-safe data access function renderPost(post: BlogPostData) { return <h1>{post.title}</h1>; } // Config includes field definitions and capability flags console.log(blogPostConfig.versioning); // true console.log(blogPostConfig.publicApi); // true

React Hooks (with 'react' target)

import { useBlogPosts, useBlogPost } from './generated/hooks'; function BlogList() { const { data, loading } = useBlogPosts({ limit: 10 }); if (loading) return <p>Loading...</p>; return ( <ul> {data?.items.map(post => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }

Remix Loaders (with 'remix' target)

import { loadBlogPosts } from './generated/loaders'; export const loader = async ({ request }: LoaderFunctionArgs) => { return loadBlogPosts({ limit: 10 }); };

GraphQL Documents

The generated .graphql files can be used with any GraphQL client or toolchain:

# From generated documents/blog-post.graphql query GetBlogPost($id: ID!) { record(id: $id) { id naturalKey data } }

Dry Run Mode

Use --dry-run to preview what files would be generated without writing anything:

foir pull --dry-run

This prints a list of all files that would be created along with a total count, which is useful for verifying configuration changes before committing to a full generation.

Last updated on