Your code that makes your service calls should normally depend on IServiceProxy
and have the concrete implementations injected at runtime. IServiceProxy
exposes the basic HTTP CRUD operations:
IServiceProxy Method | HTTP Verb |
---|---|
createViaService | POST |
readViaService | GET |
patchViaService | PATCH |
updateViaService | PUT |
deleteViaService | DELETE |
await serviceProxy.createViaService('/products/product/', { price: 500, name: 'Bicycle' });
// Payload is optional
await serviceProxy.createViaService('/foo-bar/');
const bankBalance = await serviceProxy.readViaService('/my-bank-balance/');
// Can use generics in TypeScript to specify the expected response type
const bankBalance = await serviceProxy.readViaService<number>('/my-bank-balance/');
await serviceProxy.patchViaService('/my-bank-balance/', 10000000000000000);
// Payload is optional
await serviceProxy.patchViaService('/foo-bar/');
## updateViaService
```javascript
await serviceProxy.updateViaService('/my-bank-balance/', 10000000000000000);
// Payload is optional
await serviceProxy.updateViaService('/foo-bar/');
await serviceProxy.deleteViaService('/speeding-tickets/55443/');
// Payload is optional
await serviceProxy.updateViaService('/foo-bar/', { foo: 'bar' });
The HttpServiceProxy
implentation makes real HTTP calls to your API. It is instantiated with a base URL, which is appended to all URIs specified in service calls (i.e., readViaService
, updateViaService
, deleteViaService
, and createViaService
).
import { HttpServiceProxy } from 'io-source';
const serviceProxy = new HttpServiceProxy('./my-api/');
By default, the base URL is /api/
, so if this is the path to your API, no constructor arguments are required.
import { HttpServiceProxy } from 'io-source';
const serviceProxy = new HttpServiceProxy();
In addition, it is also possible to specify a custom serializer (any object implementing stringify
and parse
). io-source provides a CircularSerializer
, which leverages Douglass Crockford's Circular JSON implementation, and can be used to deserialize objects that have circular references:
import { HttpServiceProxy, CircularSerializer } from 'io-source';
const serviceProxyWithCircularSerializer = new HttpServiceProxy('./api/', new CircularSerializer());
HttpServiceProxy
has a addGlobalRequestHeader
method that ensures every request contains a specified HTTP header, which is useful for cross-cutting concerns like authentication:
const serviceProxy = new HttpServiceProxy();
serviceProxy.addGlobalResponseHeader('authorization', `basic ${httpBasicCredentials}`);
The MockServiceProxy
implementation calls mock services that you define. For info about definining mock services, see the Defining Mock Services section.
import { MockServiceProxy } from 'io-source';
const serviceProxy = new MockServiceProxy();
MockServiceProxy
supports adding random delays to simulate the latency that would normally occur when calling a real API. When enabled, a random delay between between 0 and 1.5 seconds will be added to every mock service call. This is useful for testing that your application is providing appropriate feedback to your users while loading (e.g., a loading spinner, loading skeleton, etc.). If this option is not enabled, mock service calls are otherwise instantaneous (assuming your mock service implementation isn't slow), which is appropriate for automated tests.
const serviceProxy = new MockServiceProxy({ addRandomDelays: true });
All implementations of IServiceProxy
allow to listening to service call responses. This is useful for tasks like global error handling, logging, etc.:
serviceProxy.responseEvent.listen((response) => {
if (response.status === 500) {
window.alert(`An error occurred while calling a service: ${response.responseBody}`);
} else if (response.status === 403) {
window.alert('Access denied');
}
});