File-System API
The File-System API is the simplest way to serve mock data. Just point to a directory and plugin automatically serves files as API endpoints.
Overview
With File-System API, your directory structure becomes your API:
mock/
├── users.json → GET /api/users
├── posts/
│ └── index.json → GET /api/posts
└── products/
├── 1.json → GET /api/products/1
└── 2.json → GET /api/products/2Basic Setup
// vite.config.ts
import { defineConfig } from 'vite'
// import mockApi from '@ndriadev/vite-plugin-universal-api' //Default export
import { universalApi } from '@ndriadev/vite-plugin-universal-api' // Named export
export default defineConfig({
plugins: [
universalApi({
endpointPrefix: '/api',
fsDir: 'mock' // Points to ./mock directory
})
]
})File Lookup Strategy
When a request comes in, the plugin tries multiple strategies to find the file:
1. Exact File Match
Request: GET /api/users
Looks for: mock/users (exact match)2. Directory with index.json
Request: GET /api/posts
Looks for: mock/posts/index.json3. File with Extension
Request: GET /api/data
Looks for:
- mock/data.json
- mock/data.xml
- mock/data.txt
(any extension)Supported HTTP Methods
GET - Retrieve Data
GET /api/usersReturns the entire file content.
With Pagination:
GET /api/users?limit=10&skip=0&sortBy=name&order=ascWith Filters:
GET /api/users?status=active&minAge=18POST - Create or Query
Create new resource (file doesn't exist):
POST /api/users/123
Content-Type: application/json
{"name": "John", "email": "john@example.com"}Creates mock/users/123.json
Query existing data (file exists + pagination/filters):
POST /api/users?status=active&limit=10Returns filtered data without modifying the file.
PUT - Create or Replace
PUT /api/users/123
Content-Type: application/json
{"name": "John Updated"}- Creates file if doesn't exist (201)
- Replaces entire file if exists (200)
PATCH - Partial Update
Only works with JSON files.
Merge Patch:
PATCH /api/users/123
Content-Type: application/json
{"email": "newemail@example.com"}JSON Patch (RFC 6902):
PATCH /api/users/123
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/email", "value": "new@example.com"},
{"op": "add", "path": "/phone", "value": "555-1234"}
]DELETE - Remove Data
Delete entire file:
DELETE /api/users/123Partial delete (with filters on JSON arrays):
DELETE /api/users?status=inactiveRemoves matching items from array.
Pagination
Enable pagination in configuration:
universalApi({
endpointPrefix: '/api',
fsDir: 'mock',
pagination: {
GET: {
type: 'query-param',
limit: 'limit',
skip: 'skip',
sort: 'sortBy',
order: 'order'
}
}
})Usage:
GET /api/users?limit=10&skip=20&sortBy=name&order=descRequirements:
- ✅ File must contain JSON array
- ✅ Method must be GET, POST, HEAD, or DELETE
- ❌ Does NOT work with JSON objects or non-JSON files
Filters
Enable filtering in configuration:
universalApi({
filters: {
GET: {
type: 'query-param',
filters: [
{
key: 'status',
valueType: 'string',
comparison: 'eq'
},
{
key: 'minAge',
valueType: 'number',
comparison: 'gte'
}
]
}
}
})Usage:
GET /api/users?status=active&minAge=18Comparison Operators:
eq- equalsne- not equalsgt- greater thangte- greater than or equallt- less thanlte- less than or equalin- value in arraynin- value not in arrayregex- regular expression match (useregexFlagsfor flags, e.g."i"for case-insensitive)
File Upload
Single file upload:
POST /api/documents/report
Content-Type: multipart/form-data
(file data)⚠️ Important: Only the FIRST file is written. Multiple files are not supported.
Examples
Simple JSON Data
// mock/users.json
[
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"status": "active"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"status": "active"
}
]# Get all users
GET /api/users
# Get paginated users
GET /api/users?limit=10&skip=0
# Get active users only
GET /api/users?status=activeNested Resources
mock/
├── users/
│ ├── 1.json
│ ├── 2.json
│ └── 3.json
└── posts/
└── user-1/
├── 1.json
└── 2.jsonGET /api/users/1 → mock/users/1.json
GET /api/posts/user-1/1 → mock/posts/user-1/1.jsonBinary Files
mock/
├── images/
│ └── logo.png
└── documents/
└── report.pdfGET /api/images/logo → Returns logo.png
GET /api/documents/report → Returns report.pdfBest Practices
1. Organize by Resource
mock/
├── users/
├── posts/
├── products/
└── orders/2. Use index.json for Collections
mock/
└── users/
└── index.json # List of all users3. Individual Resources
mock/
└── users/
├── index.json # All users
├── 1.json # User 1
├── 2.json # User 2
└── 3.json # User 34. Realistic Data
Use tools like Faker.js to generate realistic mock data:
import { faker } from '@faker-js/faker'
const users = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
name: faker.person.fullName(),
email: faker.internet.email(),
avatar: faker.image.avatar(),
status: faker.helpers.arrayElement(['active', 'inactive'])
}))
// Save to mock/users.jsonDisabling the Pure File-System API
By default, any request that reaches the plugin and does not match a REST handler is automatically resolved against fsDir (the pure File-System API). You can turn off this automatic fallback with the disablePureFsApi option.
universalApi({
fsDir: 'mock',
disablePureFsApi: true, // disable the automatic FS fallback
handlers: [
{ pattern: '/api/users', method: 'GET', handle: 'FS' }
]
})When disablePureFsApi is true:
- Requests that match a handler with
handle: "FS"are still served fromfsDiras usual. - Requests that do not match any handler are handled according to
noHandledRestFsRequestsAction("404"by default) — they are never automatically mapped to files.
| Request | disablePureFsApi: false (default) | disablePureFsApi: true |
|---|---|---|
Matches a handler with handle: "FS" | Served from fsDir ✓ | Served from fsDir ✓ |
| Matches a custom handler | Custom logic ✓ | Custom logic ✓ |
| No handler matched | Automatic FS lookup ✓ | 404 / forward ✓ |
When to use this option
Use disablePureFsApi: true when you want strict, explicit control over which endpoints are exposed and you want to prevent the plugin from automatically serving any file that happens to exist under fsDir.
Limitations
- ❌ POST with multiple files not supported (only first file is written)
- ❌ Pagination/filters only work with JSON arrays
- ❌ PATCH only works with JSON files
- ❌ OPTIONS method not supported in FS mode
- ✅ All other HTTP methods fully supported
Next Steps
- REST Handlers - Add custom logic
- Pagination & Filters - Detailed configuration
- HTTP Methods - Complete reference
