SPECIAL PRICING
BLACK FRIDAY SALE
PERSONAL $99
TEAM $349
ENDS DEC 1
GET IT NOW

Mock Server

The MockServer service is an in-process HTTP mock server that intercepts Angular's HTTP client requests and returns handler-defined responses. No separate server process required.

Overview

MockServer replaces Angular's HttpBackend with a custom implementation that matches incoming requests against registered handlers. Requests that match a handler are intercepted and answered locally. Requests that don't match are forwarded to the real network via FetchBackend.

Runs Inside the App
Runs entirely inside the Angular application. No separate server process, service worker, or proxy configuration needed. It relies on Angular's Router to parse and match request URLs.
Route-Aware Path Matching
Uses Angular Router's defaultUrlMatcher so path parameters like :id and the ** wildcard work the same way as they do in your route definitions. Paths match in full: a handler for /users does not answer /users/4. When two registered paths can match the same URL, such as /users/me and /users/:id, the one registered first wins, so register specific paths before parameterized ones.
Async Handler Support
Handlers can be synchronous or async. Use await c.delay(ms) to simulate network latency inside an async handler, or rely on the global delay option for a uniform delay on all synchronous responses.
Selective Passthrough
A request is handled only when its URL starts with the configured baseURL. Relative and root-relative URLs (such as asset requests for icons/custom-icons.svg), other origins, unmatched paths, and unregistered HTTP methods all go to the real network.

Provider Setup

Add provideMockServer to the providers array of your application configuration, after provideHttpClient. Keep it behind a feature flag or environment check so it is only active during development:

import { provideMockServer } from '@/app/core/mock-server';

export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
...(isDevMode()
? [
provideMockServer({
baseURL: 'https://api.example.com',
delay: 400,
logging: {
bypass: false,
handler: true,
},
}),
]
: []),
],
};

Configuration

All options are required.

OptionTypeDescription
baseURLstring The absolute URL that the mock server handles (e.g. 'https://api.example.com'). provideMockServer throws when it does not start with http. Only requests whose URL starts with this value are considered. When it contains a path, such as 'https://example.com/api', handler paths include those segments: /api/users. Write it without a trailing slash, otherwise a request to the base URL itself ('https://api.example.com') is not handled.
delaynumber Global delay in milliseconds applied only to handlers that return synchronously. Handlers that return a Promise are never delayed globally and manage their own delay via c.delay().
logging.bypassboolean Log requests that were not matched and passed through to the network.
logging.handlerboolean Log requests that were handled by the mock server, including the response.

Registering Handlers

Inject MockServer and call the method that matches the HTTP verb you want to intercept: delete, get, head, options, patch, post, or put. Handlers are typically registered in a service:

@Service()
export class ApiMockService {
private mockServer = inject(MockServer);

constructor() {
this.mockServer.get('/users', (c) => c.json([{ id: 1, name: 'Alice' }]));
this.mockServer.post<unknown, { name: string }>('/users', (c) =>
c.json({ ...c.req.body, id: 2 })
);
this.mockServer.delete('/users/:id', (c) => c.json(null, { status: 204 }));
}
}

The handlers are registered when the service is created. Inject it from an app initializer so its constructor runs at startup, and keep the initializer inside the same condition as provideMockServer, since MockServer cannot be created without its configuration:

provideAppInitializer(() => {
inject(ApiMockService);
}),

Leading and trailing slashes are optional in both registered paths and request URLs, so /users, users, and /users/ refer to the same path. Use / to handle the base URL itself. The ** wildcard matches any remaining segments: /files/** answers /files, /files/a, and /files/a/b. Registering the same path and method again replaces the earlier handler.

Handler Context

Every handler receives a single context argument that exposes the incoming request and a set of response helpers:

PropertyTypeDescription
c.req.bodyBodyType | null The request body as passed to the HttpClient call.
c.req.paramsParamsType Path parameters extracted from the URL (e.g. { id: '4' } for a path /users/:id). Typed automatically from the path string when no type arguments are passed.
c.req.queryParamsHttpParams Query string parameters. When the Angular HttpClient call supplies HttpParams, those take precedence over the raw URL query string.
c.req.rawHttpRequest<unknown>The original Angular HTTP request object.
c.json(body, options?)Response Returns a JSON response with Content-Type: application/json. Default status is 200.
c.text(body, options?)Response Returns a plain-text response with Content-Type: text/plain. Default status is 200.
c.blob(body, options?)Response Returns a binary response. The Content-Type is derived from the Blob.type, falling back to application/octet-stream when the type is empty.
c.delay(ms)Promise<void> Awaitable helper that pauses the handler for the specified number of milliseconds. Use inside async handlers to simulate network latency.

Every register method accepts three optional type arguments: ParamsType, BodyType, and ResponseType. BodyType types c.req.body and ResponseType types the body passed to c.json(). TypeScript cannot infer some type arguments while others are given, so passing any of them turns off path parameter inference and c.req.params no longer exposes the parameters in the path. Pass ParamsType as well when such a handler reads path parameters.

Usage Examples

GET with Path Parameters

mockServer.get('/users/:id', (c) => {
const { id } = c.req.params; // typed as string

return c.json({ id, name: 'Alice' });
});

POST with a Request Body

mockServer.post<unknown, { name: string }>('/users', (c) => {
const name = c.req.body?.name ?? '';

return c.json({ id: 42, name }, { status: 201 });
});

Query Parameters

mockServer.get('/users', (c) => {
const page = c.req.queryParams.get('page') ?? '1';
const limit = c.req.queryParams.get('limit') ?? '10';

return c.json({ page, limit, items: [] });
});

Custom Status Code and Headers

mockServer.head('/users', (c) =>
c.json(null, {
headers: { 'Content-Length': '1024' },
})
);

mockServer.options('/users', (c) =>
c.json(null, {
headers: {
Allow: 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
},
})
);

Error Responses

Return any status code outside the 2xx range and the handler will emit an HttpErrorResponse to the caller, exactly like a real server error would.

mockServer.get('/users/:id', (c) => {
const { id } = c.req.params;

if (id === '0') {
return c.json({ message: 'Not found' }, { status: 404 });
}

return c.json({ id, name: 'Alice' });
});

A handler that throws, or returns a Promise that rejects, also emits an HttpErrorResponse. It carries the thrown value as error and a status of 0.

Simulated Delay

mockServer.get('/users', async (c) => {
await c.delay(1200); // simulate a slow network

return c.json([{ id: 1, name: 'Alice' }]);
});

Blob / File Download

mockServer.get('/files/:name', (c) => {
const content = new Blob(['hello, world'], { type: 'text/plain' });

return c.blob(content);
});

How It Works

  • provideMockServer replaces Angular's HttpBackend with MockServerBackend.
  • On every outgoing request, MockServerBackend asks MockServer whether the URL starts with baseURL and a handler exists for its path and method.
  • If a match is found, the handler runs and returns an Observable<HttpEvent> that is indistinguishable from a real HTTP response as far as the Angular HTTP client is concerned.
  • If no match is found, the request is forwarded to the underlying FetchBackend so real network calls continue to work normally.

Caveats

Intercepted requests never leave Angular, so they will not appear in the browser's Network tab. The mock server short-circuits the request entirely before it reaches the browser's fetch infrastructure. Use the built-in logging.handler option to inspect intercepted traffic in the browser console instead.