When we’re developing a frontend, we just might be lucky enough to have a full-fledged API, although in the real world, for the most time, we’ll get ourselves into a position where we have to develop without an API is available with all the functionality we require in the future.
There are a few tools that enable us to minimize this dependency, I remember ember
has the great ember-cli-mirage
. It provides the functionality you need, deeply integrated with ember
and makes you develop by default with a completely mocked API, which I think is a great principle to start with.
It is also great to be able to run the application without depending on external servers (imagine on a plane, on a trip without internet, or just on a computer with low memory).
You do not want your UI developers to run 3 servers just to develop a button.
Usecase
The last days we were faced with a similar issue. We're developing a screen, we had the API signature, but the API was not ready yet.
One alternative would be to just have the payload somewhere hardcoded, while we were waiting for the finished API.
Sure, we can develop your button with that but then it is not possible to implement the part of the application that does the requests. We also wanted, in future, to spend as little effort as possible to change from the mocked data to a real API.
That solution did not look right, especially when we still remember ember-cli-mirage
. Unfortunately, we can't use ember-cli-mirage
with other libraries, even though we really miss the functionality it provides.
The solution we ended up with was a combination of json-server
with route-parser
and using axios
request interceptors.
In our http-client we had calls to axios
, so, the first thing we did was to add this line.
// httpClient.js
import mockApiRequestInterceptor from './mockApiRequestInterceptor';
axios.interceptors.request.use(mockApiRequestInterceptor);
// Please keep in mind that if you use axios instance,
// you have to do it on the instance
And we've created the request interceptor
// mockApiRequestInterceptor.js
import UrlPattern from 'route-parser';
import { API_BASE_URL } from 'appConfig';
const MOCK_API_BASE_URL = 'http://localhost:3001/api';
const MOCK_URLS = [
`${API_BASE_URL}/:user/posts/:id`
];
export default (config) => {
if (!IS_DEVELOPMENT) {
return config;
}
const matchedUrl = MOCK_URLS.find(mockUrl => {
const pattern = new UrlPattern(mockUrl);
return pattern.match(config.url);
});
if (matchedUrl) {
config.url = config.url.replace(API_BASE_URL, MOCK_API_BASE_URL);
}
return config;
};
The mocked URLs would be added to MOCK_URLS
, and if there is a mocked version of them they will be redirected.
So, this code would redirect requests made from a URL that is https://mycrazyserver.com/api/asantos00/posts/123
to http://localhost:3001/api/asantos00/posts/123
, and part of the problem was solved.
After this, we took advantage of another great package json-server
(TLDR: Serves JSON files through REST routes).
We've created an npm script that runs this json-server
with a few configurations (I will explain them later).
// package.json
{
"scripts": {
"mock-api:serve": "json-server --watch mock-api/db.json --port 3001 --routes mock-api/routes.json"
}
}
And created a db.json
file
// mock-api/db.json
{
"posts": [
{
"id": 1,
"title": "Mocking an API with axios",
"author": "asantos00",
"createdAt": 1557937282,
"body": "Lorem ipsum dolor sit amet, consectetur."
},
{
"id": 2,
"title": "Whatever post",
"author": "asantos00",
"createdAt": 758851200,
"body": "Lorem ipsum dolor sit amet, consectetur."
}
]
}
It automatically creates rest endpoints for every entity on the db.json
file and watches the db file for changes.
As we said before, json-server
creates REST endpoints for entities, this means that after we have the entity post
on the db.json
, the following endpoints are created:
-
GET /posts
- Returns the list of posts -
GET /posts/:id
- Returns post with the sent id -
POST /posts
- Creates a post -
PUT /posts/:id
- Replaces the post with the sent id
The file to create custom routes is routes.json
.
// mock-api/routes.json
{
"/api/*": "/$1",
"/:user/posts/:id": "/posts?user=:user"
}
The routes file only says that whatever request comes to /:user/posts/:id
, we redirect to the automatically created endpoint that is /posts
. And yeah,json-server
also supports filtering, we use the query param user
to do it here.
It's done! Now, our app will continue making the requests to the endpoints that are implemented. But we can keep developing this page with the mocked API.
As soon as we want to use the real API, it is just a matter of removing the url from MOCK_URLS
(which can also be extracted to a separate file) in mockApiRequestInterceptor
file.
Conclusion
In the end, this solution really improves the process, helping us not being concerned about backend endpoints being done or not, while implementing all the cases in a closer to real environment.
Another nice thing about this is that it is also library agnostic as it only depends on the http-client so you can use it from react
to backbone.js
or even to react-native
.
None of this would be possible without the amazing open source packages I've used, it was just a matter of getting the pieces together for the explained use case.
What solutions do you use to solve this problem? Is it something you stumble upon frequently?
Top comments (6)
Hey there,
we are confronted with this on a daily basis and we are currently looking for the best solution for us. Right now we are trying out to use Fiddler as a proxy with it's AutoResponder feature which allows us to use any rest client library and implement down to the request. Might do a quick write up later.
That looks like a nice alternative. I tend to look for simpler solutions (that explains why I've used json-server).
After writing the article I remembered a better way to mock http requests without depending on axios, that is to basically to mock XMLHttpRequest. This way the application wouldn't know about it and it would not be bounded to axios interceptors.
Would love to read what you're writing on your solution
What do you mean mock XMLHttpRequest? Can you provide some more context? Like using this library npmjs.com/package/mock-xmlhttprequest for example?
Yes, that would be one option. Either that or something like PretenderJS does (github.com/pretenderjs/pretender/b...)
I have been using Mocklets for a year now, and its been great. Initially I used postman, but with mocklets you can link multiple responses in single API and switch between them just by a button click, which really helps in edge case testing.
Hmmm... I tend to mock APIs by using a tool called Postman There's a free version that offers that flexibility to build your frontend.