Safely Evolving Your Schema
One of the biggest fears in application development is changing your data schema. In traditional systems, a simple change—like adding or removing a field—can be a destructive operation that requires downtime, complex migration scripts, and risks data loss. FoxNose solves this with a core design principle: schema changes are always non-destructive.
When you publish a new schema version, your existing content is never automatically changed, deleted, or migrated. This guide explains how this versioning system works and how you can use it to evolve your schema safely and predictably.
How Non-Destructive Migrations Work
FoxNose's approach is built on a simple promise: your live data is sacred.
- Existing Content is Immutable: When you publish a new schema, existing resources remain untouched, continuing to conform to the schema version they were created with.
- New Content Uses the New Schema: Any new resources you create will use the new, published schema version.
- You Control the Migration: This design gives you full control over how and when you migrate old content to the new structure. No unexpected data loss, no forced downtime.
- Applies to Locales Too: This principle also applies when adding or removing locales. Deleting a locale only hides it from the API; the translated content is preserved internally unless you explicitly remove it.
Understanding the Impact of Schema Changes
Here’s how different types of schema changes affect your content and API responses.
Adding a New Field
When you add a new field (e.g., author) to a schema and publish it:
- Existing resources will not have this field. API calls for old resources won't return it.
- New resources will be created with the new field.
- To add the field to an old resource, you simply edit and re-save it, at which point you can (or must, if required) provide a value for the new field.
Removing a Field
When you remove a field (e.g., legacy_id) from a schema and publish it:
- The field disappears from the editor in the dashboard.
- The Flux API stops returning the field in standard queries. Your applications will no longer see it.
- However, the data still exists in storage. You can retrieve it using the
?raw=trueparameter in your API call, which is useful for debugging or data recovery.
Instead of deleting fields, consider marking them as private: true. This hides them from the Flux API but keeps them visible and editable in the dashboard—a safer way to deprecate fields.
Changing a Field's Properties
Changing a property like searchable or vectorizable only affects new or updated content.
- The Pain Point: You mark an existing
bodyfield asvectorizableand expect semantic search to work immediately on all your old content. - The Reality: It won't. FoxNose only generates embeddings when a resource is saved. To enable semantic search on existing content, you must re-save those resources to trigger the embedding process.
Practical Migration Strategies
Strategy 1: Additive Changes (Safest)
The simplest and safest strategy is to only add new fields and never remove or rename existing ones. Your application code should be written to handle cases where older resources may not have the new fields.
Strategy 2: Gradual Migration (for Renaming/Restructuring)
When you need to rename or restructure a field (e.g., changing summary to description):
- Add New Field: In
v2of your schema, add the newdescriptionfield alongside the oldsummaryfield. - Update Your App: Your application code should start writing to both fields, or prefer writing to the new one.
- Backfill Data: Run a script that reads each resource, copies the content from
summarytodescription, and re-saves the resource. - Deprecate Old Field: In
v3of your schema, remove thesummaryfield. - Finalize App Code: Update your application to only read from the new
descriptionfield.
Strategy 3: Bulk Re-Save for Indexing
When you make a field searchable or vectorizable, you need to trigger a re-index for all existing content. You can do this with a simple script using the Management API.
# Pseudocode to re-save all resources in a folder
# This triggers re-indexing and embedding generation.
for resource in get_all_resources(folder_id):
# Creating a new revision with the same data is enough
create_new_revision(resource)
Common Pitfalls to Avoid
-
Problem: Reusing a field key with a different data type.
- Example: Changing
pricefrom astring("19.99") to anumber(19.99). - Why it's bad: Your application will receive mixed data types, leading to errors.
- Solution: Always create a new field key (
price_numeric) and migrate the data.
- Example: Changing
-
Problem: Making an optional field required.
- Example: Changing an optional
authorfield to be required. - Why it's bad: You can no longer edit any old resources that don't have an
authorvalue, as they will fail the new validation rule. - Solution: Backfill the data to add an author to all existing resources before making the field required.
- Example: Changing an optional