flutter_mock_server
A local mock API server for Flutter and Dart projects. It supports static mock responses and schema-driven stateful APIs with CRUD routes, seeded fake data, and hot reload.
Use this package when you need a lightweight fake backend for Flutter app development, API integration testing, demos, or offline UI development.
Keywords
flutter mock server, dart mock server, fake backend for flutter, local api simulator, yaml mock api, api mocking, flutter testing backend, stateful mock api
Mental Model
flutter_mock_server has three main building blocks:
modelsdescribe the shape of generated data.storeskeep in-memory collections of generated records based on those models.routeseither return static responses or perform CRUD actions against stores.
Use it in one of two ways:
- Static mode: return a fixed inline body or a JSON file.
- Stateful mode: define models and stores, then let routes create, read, update, list, and delete records.
Important behavior:
- Store data is in memory only.
- Restarting the server or reloading
mock.yamlrebuilds stores from the configured seed. - If a
seedis provided, generated records are deterministic across reloads.
What is new in 1.0.0
- Schema-driven models for reusable fake data definitions.
- Seeded in-memory stores so responses stay deterministic across reloads.
- CRUD actions with path parameter matching.
- Query-based filtering, sorting, and pagination for list endpoints.
- Request-aware templating with access to path params, query params, and JSON body values.
- Existing static YAML responses, delays, error injection, and hot reload still work.
Installation
dart pub global activate flutter_mock_server
Or run it directly from the package:
dart run flutter_mock_server start
Commands
Initialize a project
flutter_mock init
Creates a starter mock.yaml that demonstrates models, stores, and CRUD routes.
Start the server
flutter_mock start
Options:
--config Path to the YAML config file. Defaults to mock.yaml.
--host Host interface to bind. Defaults to localhost.
--port Port to bind. Defaults to 8080.
Validate configuration
flutter_mock validate
Validation output now includes route, model, and store counts.
Quick Start
seed: 42
models:
User:
id: uuid
name: name
email: email
role:
enum: [admin, member, viewer]
age:
type: int
min: 18
max: 60
stores:
users:
model: User
count: 8
routes:
- path: /users
method: GET
action: list
store: users
- path: /users/:id
method: GET
action: get
store: users
- path: /users
method: POST
action: create
store: users
- path: /users/:id
method: PUT
action: update
store: users
- path: /users/:id
method: DELETE
action: delete
store: users
- path: /session
method: POST
response:
status: 201
body:
token: "{{uuid}}"
email: "{{request.body.email}}"
message: Signed in
What this config does:
- seeds a
usersstore with 8 generatedUserrecords - exposes CRUD endpoints for
/usersand/users/:id - keeps generated data stable across reloads because
seed: 42is set - adds a static
/sessionroute that reads from the incoming request body
Start the server:
flutter_mock start
Then try:
curl http://localhost:8080/users
curl http://localhost:8080/users?role=admin&limit=2
curl -X POST http://localhost:8080/users \
-H "content-type: application/json" \
-d '{"name":"Morgan","email":"morgan@sample.app","role":"member","age":29}'
Configuration
Models
A model is a reusable schema. Each field can be a built-in generator, a constrained numeric field, an enum, another model name, an explicit array schema, or a ranged date.
In practice:
- use a built-in type like
emailoruuidfor generated values - use another model name to nest an object
- use
type: arraywithitemsto generate lists - use
examplewhen you want a fixed value instead of a generated one
Supported built-in field types:
uuidnameemailtimestampdateintdoubleboolwordsentencestring
Examples:
models:
Review:
author: name
createdAt:
type: date
from: 2026-01-01T00:00:00Z
to: 2026-01-31T23:59:59Z
ProductVariant:
sku: word
stock:
type: int
min: 1
max: 20
Product:
id: uuid
name: sentence
price:
type: double
min: 9.99
max: 199.99
status:
enum: [draft, active, archived]
variants:
type: array
count: 2
items:
type: ProductVariant
latestReview:
type: Review
Array fields use type: array with count and an items definition. Date fields use type: date with optional from, to, and format: date.
Stores
A store seeds an in-memory collection from a model.
Think of a store as a temporary local table. Routes can list records from it, fetch one by id, create new ones, update them, and delete them.
stores:
products:
model: Product
count: 25
primary_key: id
CRUD Actions
A route becomes stateful when it uses action and store.
Supported actions:
listgetcreateupdatedelete
Path parameters use :paramName syntax.
routes:
- path: /products
method: GET
action: list
store: products
- path: /products/:id
method: GET
action: get
store: products
Typical behavior:
listreturns an array of recordsgetreturns a single record matched by the route parametercreatemerges generated model values with the JSON request bodyupdatepatches an existing record with the JSON request bodydeleteremoves the record and returns a confirmation payload
Example flow:
curl -X POST http://localhost:8080/users \
-H "content-type: application/json" \
-d '{"name":"Morgan","email":"morgan@sample.app","role":"member","age":29}'
Example response:
{
"id": "a786ac22-0643-4983-81f1-884dfb30eaa1",
"name": "Morgan",
"email": "morgan@sample.app",
"role": "member",
"age": 29
}
Then:
curl http://localhost:8080/users/a786ac22-0643-4983-81f1-884dfb30eaa1
curl -X PUT http://localhost:8080/users/a786ac22-0643-4983-81f1-884dfb30eaa1 \
-H "content-type: application/json" \
-d '{"role":"admin"}'
curl -X DELETE http://localhost:8080/users/a786ac22-0643-4983-81f1-884dfb30eaa1
Query Behavior
List routes support:
- exact-match filtering with arbitrary query params such as
?role=admin - sorting with
?sort=name&order=asc - pagination with
?page=2&limit=20
Request-Aware Templates
Templates can read data from the request and the current record.
Supported placeholders include:
{{uuid}}{{name}}{{email}}{{timestamp}}{{request.path.id}}{{request.query.role}}{{request.body.email}}{{record.name}}
Example:
routes:
- path: /session
method: POST
response:
status: 201
body:
token: "{{uuid}}"
email: "{{request.body.email}}"
userId: "{{request.path.id}}"
These bindings are useful when you want a mostly static route to still echo request data, such as login, search, upload, or workflow endpoints.
Static Responses Still Supported
You can continue using the original file-based or inline response mode.
Use static mode when you just need a fixed response quickly. Use stateful mode when your app needs realistic create/edit/delete flows or data that stays consistent across requests.
routes:
- path: /health
method: GET
response:
body:
status: ok
generatedAt: "{{timestamp}}"
- path: /users
method: GET
response:
file: data/users.json
Advanced response options still work:
statusheadersdelay_mserror.rateerror.statuserror.bodyerror.file
Example
Run the end-to-end example:
dart run example/flutter_mock_server_example.dart
The example:
- starts a schema-driven server on a free local port
- lists seeded users with query filtering
- creates a user through
POST /users - reads and updates the created user through
GETandPUT - issues a request-aware static response through
POST /session - deletes the created user through
DELETE
Development
dart pub get
dart analyze
dart run flutter_mock_server validate
dart run example/flutter_mock_server_example.dart