Skip to content

Workspaces

Each workspace is an isolated mock API environment with its own subdomain, database, and configuration.

Overview

A workspace is the fundamental unit in Testfly. When you create a workspace, you get:

  • A dedicated subdomain: {slug}.mock.testfly.com
  • An isolated SQLite database with your mock data
  • Full CRUD operations on every resource
  • Configurable simulated conditions (latency, errors, CORS)

Workspaces are scoped to your organization. All team members with org access can manage workspaces.

curl -X POST https://api.testfly.com/api/workspaces \
  -H "X-API-Key: tfk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "E-Commerce API",
    "slug": "ecommerce",
    "rowCount": 1000,
    "seedValue": 42
  }'

Data generation

When you upload an OpenAPI spec, Testfly:

  1. Parses your spec — extracts schemas, relationships, and routes
  2. Emits .NET types at runtime with table and column attributes
  3. Creates SQLite tables with PK/FK constraints
  4. Generates data in topological order (parent tables first, then children)

Data generation uses Cufflink, a semantic fake data engine that understands column names and types. A column named email gets realistic email addresses. A column named price gets sensible decimal values. Foreign keys always point to real records in the referenced table.


Configuring row counts

The rowCount setting controls how many records are generated per resource table. Choose based on your use case:

Row countUse caseGeneration time
10Quick prototyping, UI development, demosInstant
100 (default)General development, manual testing, API exploration<1 second
1,000Integration testing, pagination, filtering/sortingA few seconds
10,000Performance testing, realistic data volumes (Pro max)~10 seconds

Set the row count when creating or updating a workspace:

# Create with 10,000 rows per table
curl -X POST .../api/workspaces \
  -d '{ "name": "Load Test", "slug": "loadtest", "rowCount": 10000 }'

# Update to 1 million rows
curl -X PUT .../api/workspaces/{id} \
  -d '{ "rowCount": 1000000 }'

# Regenerate data with the new count
curl -X POST .../api/workspaces/{id}/regenerate

After changing rowCount, call /regenerate to rebuild the database with the new volume.

Per-resource overrides

Some resources need different volumes. A realistic e-commerce mock might want 100 users, 50 products, and 10,000 orders. Per-resource rowCountOverride is available on the MockResource entity.


Reproducible data with seed values

Set a seedValue to get identical data every time:

curl -X POST .../api/workspaces \
  -d '{ "name": "CI Mock", "slug": "ci-test", "rowCount": 100, "seedValue": 42 }'

This is essential for:

  • CI/CD pipelines — assert against known data values
  • Snapshot testing — compare mock responses deterministically
  • Bug reproduction — recreate the exact dataset that triggered an issue

Same spec + same seed + same row count = identical mock API every time.


CRUD operations

Every resource gets full CRUD endpoints. All operations hit a real SQLite database — data persists until you regenerate or delete.

Pagination

GET /pets?page=3&pageSize=50

{
  "data": [ ... ],
  "meta": { "page": 3, "pageSize": 50, "totalCount": 10000, "totalPages": 200 }
}

Filtering

# All active cats
GET /pets?status=active&species=cat

Sorting

GET /pets?sortBy=name&sortDesc=true

Write operations

# Create — auto-generates UUID id
POST /pets { "name": "Buddy", "species": "dog" }
→ 201 Created + Location header

# Update — partial updates supported
PUT /pets/{id} { "name": "Buddy Jr" }
→ 200 OK

# Delete
DELETE /pets/{id}
→ 204 No Content

FK constraints are enforced — you can't create a pet with an owner_id that doesn't exist.


Simulated conditions

Configure workspace settings to test real-world conditions:

Latency

{ "settings": { "latencyMs": 500 } }

Every request delayed by 500ms. Test loading states, timeouts, and debouncing.

Error rate

{ "settings": { "errorRate": 0.1 } }

10% of requests return 500. Test retry logic and error handling.

CORS

{ "settings": { "corsOrigins": ["https://localhost:3000"] } }

Combined

PUT /api/workspaces/{id}
{
  "settings": {
    "latencyMs": 200,
    "errorRate": 0.05,
    "corsOrigins": ["https://app.example.com"],
    "authEnabled": false
  }
}

Workspace lifecycle

                   pause
                   ↓
Provisioning → Active ↔ Paused
         |
         ↓
       Error              Deleted
StatusMock APIPlatform APITransitions
Provisioning503Read-only→ Active or Error
ActiveLiveFull accessPause, Delete, Regenerate
Paused503Full accessResume, Delete
Error503Read-onlyUpload new spec to retry
DeletedGoneGoneTerminal

Regenerating data

POST /api/workspaces/{id}/regenerate

Drops all data, regenerates from the active spec. Use to reset between test runs, apply a new rowCount, or get fresh data after changing the spec.


Using workspaces in CI/CD

Testfly workspaces are designed for automation. A typical CI pipeline:

# 1. Create a workspace for this test run
WORKSPACE=$(curl -s -X POST .../api/workspaces \
  -H "X-API-Key: $TESTFLY_KEY" \
  -d "{
    \"name\": \"CI Run $GITHUB_SHA\",
    \"slug\": \"ci-$(echo $GITHUB_SHA | head -c 8)\",
    \"rowCount\": 100,
    \"seedValue\": 12345
  }" | jq -r '.id')

# 2. Upload your spec
curl -X POST .../api/workspaces/$WORKSPACE/specs \
  -H "X-API-Key: $TESTFLY_KEY" \
  -d "{ \"rawSpec\": \"$(cat openapi.yaml)\", \"versionLabel\": \"ci\" }"

# 3. Wait for provisioning
sleep 5

# 4. Run integration tests against the mock
MOCK_URL="https://ci-$(echo $GITHUB_SHA | head -c 8).mock.testfly.com"
npm test -- --api-url=$MOCK_URL

# 5. Clean up
curl -X DELETE .../api/workspaces/$WORKSPACE \
  -H "X-API-Key: $TESTFLY_KEY"

Use seedValue for deterministic assertions and rowCount to control data volume.

Ready to create your first workspace?

Start free. No credit card required.