Package Exports
- angular-in-memory-web-api
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (angular-in-memory-web-api) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Angular in-memory-web-api
An in-memory web api for Angular demos and tests that emulates CRUD operations over a RESTy API.
It intercepts Angular Http and HttpClient requests that would otherwise go to the remote server and redirects them to an in-memory data store that you control.
v0.4.0 supports HttpClient!
Release v0.4.0 (8 Sept 2017) is a major overhaul of this library.
You don't have to change your existing application code if your app uses this library without customizations.
This v0.4.0 release has breaking changes that affect developers who used the customization features. We're pretty sure you'll think these changes are worthwhile.
Read this README and the CHANGELOG to learn what's new. The new unit tests are worth a look as well.
If you’re loading application files with SystemJS, you’ll have to configure it to load Angular’s
umd.jsforHttpModule. To see how, look for the following line in thesrc/systemjs.config.jsof this project:
'@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js'You've already made this change if you are using
HttpClienttoday.If you’re sticking with the original Angular
Httpmodule, you must make this change anyway! Your app will break as soon as you runnpm installand it installs v0.4.0.If you're using webpack (as CLI devs do), you don't have to worry about this stuff because webpack bundles the dependencies for you.
Use cases
Demo apps that need to simulate CRUD data persistence operations without a real server. You won't have to build and start a test server.
Whip up prototypes and proofs of concept.
Share examples with the community in a web coding environment such as Plunker or CodePen. Create Angular issues and StackOverflow answers supported by live code.
Simulate operations against data collections that aren't yet implemented on your dev/test server. You can pass requests thru to the dev/test server for collections that are supported.
Write unit test apps that read and write data. Avoid the hassle of intercepting multiple http calls and manufacturing sequences of responses. The in-memory data store resets for each test so there is no cross-test data pollution.
End-to-end tests. If you can toggle the app into test mode using the in-mem web api, you won't disturb the real database. This can be especially useful for CI (continuous integration) builds.
LIMITATIONS
The in-memory-web-api exists primarily to support the Angular documentation. It is not supposed to emulate every possible real world web API and is not intended for production use.
Most importantly, it is always experimental. We will make breaking changes and we won't feel bad about it because this is a development tool, not a production product. We do try to tell you about such changes in the
CHANGELOG.mdand we fix bugs as fast as we can.
HTTP request handling
This in-memory web api service processes an HTTP request and
returns an Observable of HTTP Response object
in the manner of a RESTy web api.
It natively handles URI patterns in the form :base/:collectionName/:id?
Examples:
// for requests to an `api` base URL that gets heroes from a 'heroes' collection
GET api/heroes // all heroes
GET api/heroes/42 // the hero with id=42
GET api/heroes?name=^j // 'j' is a regex; returns heroes whose name starting with 'j' or 'J'
GET api/heroes.json/42 // ignores the ".json"
Commands
The service also accepts "commands" that can, for example, reconfigure the service and reset the database.
When the last segment of the api base path is "commands", the collectionName is treated as the command.
Example URLs:
commands/resetdb // Reset the "database" to its original state
commands/config // Get or update this service's config objectCommands are "hot", meaning they are always executed immediately whether or not someone subscribes to the returned observable.
Usage:
http.post('commands/resetdb', undefined);
http.get('commands/config');
http.post('commands/config', '{"delay":1000}');Basic usage
Create an InMemoryDataService class that implements InMemoryDbService.
At minimum it must implement createDb which
creates a "database" hash whose keys are collection names
and whose values are arrays of collection objects to return or update.
For example:
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemHeroService implements InMemoryDbService {
createDb() {
let heroes = [
{ id: 1, name: 'Windstorm' },
{ id: 2, name: 'Bombasto' },
{ id: 3, name: 'Magneta' },
{ id: 4, name: 'Tornado' }
];
return {heroes};
}
}This library currently assumes that every collection has a primary key called
id.
Register this module and your data store service implementation in AppModule.imports
calling the forRoot static method with this service class and optional configuration object:
import { HttpClientModule } from '@angular/common/http';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemHeroService } from '../app/hero.service';
@NgModule({
imports: [
HttpClientModule,
InMemoryWebApiModule.forRoot(InMemHeroService),
...
],
...
})
export class AppModule { ... }Notes
Always import the
InMemoryWebApiModuleafter theHttpClientModuleto ensure that the in-memory backed provider supersedes the Angular version.You can setup the in-memory web api within a lazy loaded feature module by calling the
.forFeaturemethod as you would.forRoot.You can still use the in-memory web api with the older
Httpmodule.import { HttpModule } from '@angular/http'; import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { InMemHeroService } from '../app/hero.service'; @NgModule({ imports: [ HttpModule, InMemoryWebApiModule.forRoot(InMemHeroService), ... ], ... }) export class AppModule { ... }
The
createDbmethod can be synchronous or asynchronous. so you can initialize your in-memory database service from a JSON file. Return the database object, an observable of that object, or a promise of that object. The in-mem web api service callscreateDb(a) when it handles the firstHttpClient(orHttp) request and (b) when it receives aPOST resetdbrequest.
In-memory web api examples
The tests (src/app/*.spec.ts files) in the github repo are a good place to learn how to setup and use this in-memory web api library.
See also the example source code in the official Angular.io documentation such as the HttpClient guide and the Tour of Heroes.
Advanced Features
Some features are not readily apparent in the basic usage example.
The InMemoryBackendConfigArgs defines a set of options. Add them as the second forRoot argument:
InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 500 }),Read the InMemoryBackendConfigArgs interface to learn about these options.
Request evaluation order
This service can evaluate requests in multiple ways depending upon the configuration. Here's how it reasons:
- If it looks like a command, process as a command
- If the HTTP method is overridden, try the override.
- If the resource name (after the api base path) matches one of the configured collections, process that
- If not but the
Config.passThruUnknownUrlflag istrue, try to pass the request along to a real XHR. - Return a 404.
See the handleRequest method implementation for details.
Default delayed response
By default this service adds a 500ms delay (see InMemoryBackendConfig.delay)
to all requests to simulate round-trip latency.
You can eliminate that or extend it by setting a different value:
InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 0 }), // no delay
InMemoryWebApiModule.forRoot(InMemHeroService, { delay: 1500 }), // 1.5 second delaySimple query strings
Pass custom filters as a regex pattern via query string. The query string defines which property and value to match.
Format: /app/heroes/?propertyName=regexPattern
The following example matches all names start with the letter 'j' or 'J' in the heroes collection.
/app/heroes/?name=^j
Search pattern matches are case insensitive by default. Set
config.caseSensitiveSearch = trueif needed.
Pass thru to a live server
If an existing, running remote server should handle requests for collections
that are not in the in-memory database, set Config.passThruUnknownUrl: true.
Then this service will forward unrecognized requests to the remote server
via the Angular default XHR backend (it depends on whether your using Http or HttpClient).
parseRequestUrl and your override
The parseRequestUrl parses the request URL into a ParsedRequestUrl object.
ParsedRequestUrl is a public interface whose properties guide the in-memory web api
as it processes the request.
Default parseRequestUrl
Default parsing depends upon certain values of config: apiBase, host, and urlRoot.
Read the source code for the complete story.
Configuring the apiBase yields the most interesting changes to parseRequestUrl behavior:
For
apiBase=undefinedandurl='http://localhost/api/customers/42'{apiBase: 'api/', collectionName: 'customers', id: '42', ...}For
apiBase='some/api/root/'andurl='http://localhost/some/api/root/customers'{ apiBase: 'some/api/root/', collectionName: 'customers', id: undefined, ... }For
apiBase='/'andurl='http://localhost/customers'{ apiBase: '/', collectionName: 'customers', id: undefined, ... }
The actual api base segment values are ignored. Only the number of segments matters. The following api base strings are considered identical: 'a/b' ~ 'some/api/' ~ `two/segments'
This means that URLs that work with the in-memory web api may be rejected by the real server.
Custom parseRequestUrl
You can override the default parser by implementing a parseRequestUrl method in your InMemoryDbService.
The service calls your method with two arguments.
url- the request URL stringrequestInfoUtils- utility methods in aRequestInfoUtilitiesobject, including the default parser. Note that some values have not yet been set as they depend on the outcome of parsing.
Your method must either return a ParsedRequestUrl object or null|undefined,
in which case the service uses the default parser.
In this way you can intercept and parse some URLs and leave the others to the default parser.
Custom genId
Collection items are presumed to have a primary key property called id.
When you can specify the id when you add a new item;
the service does not check for uniqueness.
If you do not specify the id, the service generates one via the genId method.
You can override the default generator with a genId method in your InMemoryDbService.
Your method receives the new item's collection and should return the generated id.
If your generator returns null|undefined, the service uses the default generator.
responseInterceptor
You can morph the response returned by the services default HTTP methods. A typical reason to intercept is to add a header that your application is expecting.
To intercept responses, add a responseInterceptor method to your InMemoryDbService class.
The service calls your interceptor like this:
responseOptions = this.responseInterceptor(responseOptions, requestInfo);HTTP method interceptors
If you make requests this service can't handle but still want an in-memory database to hold values,
override the way this service handles any HTTP method by implementing a method in
your InMemoryDbService that does the job.
The InMemoryDbService method name must be the same as the HTTP method name but all lowercase.
This service calls it with an HttpMethodInterceptorArgs object.
For example, your HTTP GET interceptor would be called like this:
e.g., yourInMemDbService["get"](interceptorArgs).
Your method must return either:
Observable<Response>- your code has handled the request and the response is available from this observable. It should be "cold".null/undefined- your code decided not to intervene, perhaps because you wish to intercept only certain paths for the given HTTP method. The service continues with its default processing of the HTTP request.
The HttpMethodInterceptorArgs (as of this writing) are:
requestInfo: RequestInfo; // parsed request
db: Object; // the current in-mem database collections
config: InMemoryBackendConfigArgs; // the current config
passThruBackend: ConnectionBackend; // pass through backend, if it exists
/**
* Create a cold response Observable from a factory for ResponseOptions
* the same way that the in-mem backend service does.
* @param resOptionsFactory - creates ResponseOptions when observable is subscribed
* @param withDelay - if true (default), add simulated latency delay from configuration
*/
createResponse$: (resOptionsFactory: () => ResponseOptions) => Observable<any>;In-memory Web Api Examples
The file src/app/hero-in-mem-data.service.ts is an example of a Hero-oriented InMemoryDbService,
such as you might see in an HTTP sample in the Angular documentation.
To try it, add the following line to AppModule.imports
InMemoryWebApiModule.forRoot(HeroInMemDataService)For examples of overriding service methods,
see the src/app/hero-in-mem-data-override.service.ts class.
Add the following line to AppModule.imports to see this version of the data service in action:
InMemoryWebApiModule.forRoot(HeroInMemDataOverrideService)The tests (see below) exercise these examples.
Build Instructions
Mostly gulp driven.
The following describes steps for updating from one Angular version to the next
This is essential even when there are no changes of real consequence. Neglecting to synchronize Angular 2 versions triggers typescript definition duplication error messages when compiling your application project.
gulp bump- up the package version numberupdate
CHANGELOG.MDto record the changeupdate the dependent version(s) in
package.jsonnpm installthe new package(s) (make sure they really do install!)
npm list --depth=0consider updating typings, install individually/several:
npm install @types/jasmine @types/node --save-devgulp clean- clear out all generatedtextnpm run buildto confirm the project compiles w/o error (sanity check)npm testto build and run tests (see "Testing" below)gulp buildcommit and push
npm publishFix and validate angular.io docs samples
Add two tags to the release commit with for unpkg
- the version number
- 'latest'
Testing
The "app" for this repo is not a real app.
It's an Angular data service (HeroService) and a bunch of tests.
Note that the
tsconfig.jsonproduces acommonjsmodule. That's what Angular specs require. But when building for an app, it should be aes2015module, as is thetsconfig-ngc.jsonfor AOT-ready version of this library.
These tests are a work-in-progress, as tests often are.
The src/ folder is divided into
app/- the test "app" and its testsin-mem/- the source code for the in-memory web api library
A real app would reference the in-memory web api node module; these tests reference the library source files.
The karma-test-shim.js add in-mem to the list of app folders that SystemJS should resolve.
Rollup
The gulp "umd" task runs rollup for tree-shaking.
I don't remember it ever working without a lot of warnings.
In v.0.4.x, updated to v.0.49 ... which required updating of the rollup.config.js.
Still weirdly runs an unspecified cjs rollup config first that I can’t find (which produces numerous warnings) before doing the right thing and running the umd config.
Also does not work if follow instructions and use the output property of rollup.config.js; does work when config it “wrong” and put the options in the root.
Ignoring these issues for now.