Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Fastapi | 58,725 | 138 | 1,143 | 17 hours ago | 147 | May 14, 2022 | 522 | mit | Python | |
FastAPI framework, high performance, easy to learn, fast to code, ready for production | ||||||||||
Httpie | 27,856 | 1,645 | 42 | 13 days ago | 55 | May 06, 2022 | 146 | bsd-3-clause | Python | |
🥧 HTTPie for Terminal — modern, user-friendly command-line HTTP client for the API era. JSON support, colors, sessions, downloads, plugins & more. | ||||||||||
Ky | 9,846 | 875 | 260 | 20 days ago | 53 | September 03, 2022 | 48 | mit | TypeScript | |
🌳 Tiny & elegant JavaScript HTTP client based on the browser Fetch API | ||||||||||
Api Platform | 7,919 | 5 days ago | 37 | February 12, 2018 | 559 | mit | TypeScript | |||
Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time. | ||||||||||
Rest Assured | 6,349 | 7,364 | 395 | 2 months ago | 29 | September 09, 2022 | 503 | apache-2.0 | Java | |
Java DSL for easy testing of REST services | ||||||||||
Wiremock | 5,580 | 1,626 | 752 | a day ago | 104 | September 10, 2020 | 313 | apache-2.0 | Java | |
A tool for mocking HTTP services | ||||||||||
Kemal | 3,485 | 2 months ago | 15 | mit | Crystal | |||||
Fast, Effective, Simple Web Framework | ||||||||||
Php Curl Class | 3,152 | 690 | 352 | 3 days ago | 66 | September 24, 2022 | 3 | unlicense | PHP | |
PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs | ||||||||||
Flask Restplus | 2,558 | 863 | 119 | 2 years ago | 35 | August 12, 2019 | 380 | other | Python | |
Fully featured framework for fast, easy and documented API development with Flask | ||||||||||
Httpexpect | 2,202 | 70 | 110 | 4 days ago | 23 | July 20, 2021 | 34 | mit | Go | |
End-to-end HTTP and REST API testing for Go. |
Intercept AJAX calls to fake a REST server based on JSON data. Use it on top of Sinon.js (for XMLHTTPRequest
) or fetch-mock (for fetch
) to test JavaScript REST clients on the browser side (e.g. single page apps) without a server.
<script src="/path/to/FakeRest.min.js"></script>
<script src="/path/to/sinon.js"></script>
<script type="text/javascript">
var data = {
'authors': [
{ id: 0, first_name: 'Leo', last_name: 'Tolstoi' },
{ id: 1, first_name: 'Jane', last_name: 'Austen' }
],
'books': [
{ id: 0, author_id: 0, title: 'Anna Karenina' },
{ id: 1, author_id: 0, title: 'War and Peace' },
{ id: 2, author_id: 1, title: 'Pride and Prejudice' },
{ id: 3, author_id: 1, title: 'Sense and Sensibility' }
],
'settings': {
language: 'english',
preferred_format: 'hardback',
}
};
// initialize fake REST server
var restServer = new FakeRest.Server();
restServer.init(data);
// use sinon.js to monkey-patch XmlHttpRequest
var server = sinon.fakeServer.create();
server.respondWith(restServer.getHandler());
</script>
import fetchMock from 'fetch-mock';
import FakeRest from 'fakerest';
var data = {
'authors': [
{ id: 0, first_name: 'Leo', last_name: 'Tolstoi' },
{ id: 1, first_name: 'Jane', last_name: 'Austen' }
],
'books': [
{ id: 0, author_id: 0, title: 'Anna Karenina' },
{ id: 1, author_id: 0, title: 'War and Peace' },
{ id: 2, author_id: 1, title: 'Pride and Prejudice' },
{ id: 3, author_id: 1, title: 'Sense and Sensibility' }
],
'settings': {
language: 'english',
preferred_format: 'hardback',
}
};
const restServer = new FakeRest.FetchServer('http://localhost:3000');
restServer.init(data);
fetchMock.mock('begin:http://localhost:3000', restServer.getHandler());
FakeRest will now intercept every XmlHttpRequest
to the REST server. The handled routes for collections of items are:
GET /:resource
POST /:resource
GET /:resource/:id
PUT /:resource/:id
PATCH /:resource/:id
DELETE /:resource/:id
The handled routes for single items are:
GET /:resource
PUT /:resource
PATCH /:resource
Let's see an example:
// Query the fake REST server
var req = new XMLHttpRequest();
req.open("GET", "/authors", false);
req.send(null);
console.log(req.responseText);
// [
// {"id":0,"first_name":"Leo","last_name":"Tolstoi"},
// {"id":1,"first_name":"Jane","last_name":"Austen"}
// ]
var req = new XMLHttpRequest();
req.open("GET", "/books/3", false);
req.send(null);
console.log(req.responseText);
// {"id":3,"author_id":1,"title":"Sense and Sensibility"}
var req = new XMLHttpRequest();
req.open("GET", "/settings", false);
req.send(null);
console.log(req.responseText);
// {"language:"english","preferred_format":"hardback"}
var req = new XMLHttpRequest();
req.open("POST", "/books", false);
req.send(JSON.stringify({ author_id: 1, title: 'Emma' }));
console.log(req.responseText);
// {"author_id":1,"title":"Emma","id":4}
// restore native XHR constructor
server.restore();
Tip: The fakerServer
provided by Sinon.js is available as a standalone library, without the entire stubbing framework. Simply add the following bower dependency:
devDependencies: {
"sinon-server": "http://sinonjs.org/releases/sinon-server-1.14.1.js"
}
FakeRest is available through npm and Bower:
# If you use Bower
bower install fakerest --save-dev
# If you use npm
npm install fakerest --save-dev
FakeRest defines a REST flavor, described below. It is inspired by commonly used ways how to handle aspects like filtering and sorting.
GET /foo
returns a JSON array. It accepts three query parameters: filter
, sort
, and range
. It responds with a status 200 if there is no pagination, or 206 if the list of items is paginated. The response contains a mention of the total count in the Content-Range
header.
GET /books?filter={"author_id":1}&embed=["author"]&sort=["title","desc"]&range=[0-9]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
]
The filter
param must be a serialized object literal describing the criteria to apply to the search query.
GET /books?filter={"author_id":1} // return books where author_id is equal to 1
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// array values are possible
GET /books?filter={"id":[2,3]} // return books where id is in [2,3]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// use the special "q" filter to make a full-text search on all text fields
GET /books?filter={"q":"and"} // return books where any of the book properties contains the string 'and'
HTTP 1.1 200 OK
Content-Range: items 0-2/3
Content-Type: application/json
[
{ "id": 1, "author_id": 0, "title": "War and Peace" },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility" }
]
// use _gt, _gte, _lte, _lt, or _neq suffix on filter names to make range queries
GET /books?filter={"price_lte":20} // return books where price is less than or equal to 20
GET /books?filter={"price_gt":20} // return books where price is greater than 20
// when the filter object contains more than one property, the criteria combine with an AND logic
GET /books?filter={"published_at_gte":"2015-06-12","published_at_lte":"2015-06-15"} // return books published between two dates
The embed
param sets the related objects or collections to be embedded in the response.
// embed author in books
GET /books?embed=["author"]
HTTP 1.1 200 OK
Content-Range: items 0-3/4
Content-Type: application/json
[
{ "id": 0, "author_id": 0, "title": "Anna Karenina", "author": { "id": 0, "first_name": "Leo", "last_name": "Tolstoi" } },
{ "id": 1, "author_id": 0, "title": "War and Peace", "author": { "id": 0, "first_name": "Leo", "last_name": "Tolstoi" } },
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } },
{ "id": 3, "author_id": 1, "title": "Sense and Sensibility", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
]
// embed books in author
GET /authors?embed=["books"]
HTTP 1.1 200 OK
Content-Range: items 0-1/2
Content-Type: application/json
[
{ id: 0, first_name: 'Leo', last_name: 'Tolstoi', books: [{ id: 0, author_id: 0, title: 'Anna Karenina' }, { id: 1, author_id: 0, title: 'War and Peace' }] },
{ id: 1, first_name: 'Jane', last_name: 'Austen', books: [{ id: 2, author_id: 1, title: 'Pride and Prejudice' }, { id: 3, author_id: 1, title: 'Sense and Sensibility' }] }
]
// you can embed several objects
GET /authors?embed=["books","country"]
The sort
param must be a serialized array literal defining first the property used for sorting, then the sorting direction.
GET /author?sort=["date_of_birth","asc"] // return authors, the oldest first
GET /author?sort=["date_of_birth","desc"] // return authors, the youngest first
The range
param defines the number of results by specifying the rank of the first and last result. The first result is #0.
GET /books?range=[0-9] // return the first 10 books
GET /books?range=[10-19] // return the 10 next books
POST /foo
returns a status 201 with a Location
header for the newly created resource, and the new resource in the body.
POST /books
{ "author_id": 1, "title": "Emma" }
HTTP 1.1 201 Created
Location: /books/4
Content-Type: application/json
{ "author_id": 1, "title": "Emma", "id": 4 }
GET /foo/:id
returns a JSON object, and a status 200, unless the resource doesn't exist
GET /books/2
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice" }
The embed
param sets the related objects or collections to be embedded in the response.
GET /books/2?embed=['author']
HTTP 1.1 200 OK
Content-Type: application/json
{ "id": 2, "author_id": 1, "title": "Pride and Prejudice", "author": { "id": 1, "first_name": "Jane", "last_name": "Austen" } }
PUT /foo/:id
returns the modified JSON object, and a status 200, unless the resource doesn't exist
DELETE /foo/:id
returns the deleted JSON object, and a status 200, unless the resource doesn't exist
If the REST flavor you want to simulate differs from the one chosen for FakeRest, no problem: request and response interceptors will do the conversion (see below).
Note that all of the above apply only to collections. Single objects respond to GET /bar
, PUT /bar
and PATCH /bar
in a manner identical to those operations for /foo/:id
, including embedding. POST /bar
and DELETE /bar
are not enabled.
Operators are specified as suffixes on each filtered field. For instance, applying the _lte
operator on the price
field for the books
resource is done by like this:
GET /books?filter={"price_lte":20} // return books where price is less than or equal to 20
_eq
: check for equality on simple values:
GET /books?filter={"price_eq":20} // return books where price is equal to 20
_neq
: check for inequality on simple values
GET /books?filter={"price_neq":20} // return books where price is not equal to 20
_eq_any
: check for equality on any passed values
GET /books?filter={"price_eq_any":[20, 30]} // return books where price is equal to 20 or 30
_neq_any
: check for inequality on any passed values
GET /books?filter={"price_neq_any":[20, 30]} // return books where price is not equal to 20 nor 30
_inc_any
: check for items that includes any of the passed values
GET /books?filter={"authors_inc_any":['William Gibson', 'Pat Cadigan']} // return books where authors includes either 'William Gibson' or 'Pat Cadigan' or both
_q
: check for items that contains the provided text
GET /books?filter={"author_q":['Gibson']} // return books where author includes 'Gibson' not considering the other fields
_lt
: check for items that has a value lower than the provided value
GET /books?filter={"price_lte":100} // return books that have a price lower that 100
_lte
: check for items that has a value lower or equal than the provided value
GET /books?filter={"price_lte":100} // return books that have a price lower or equal to 100
_gt
: check for items that has a value greater than the provided value
GET /books?filter={"price_gte":100} // return books that have a price greater that 100
_gte
: check for items that has a value greater or equal than the provided value
GET /books?filter={"price_gte":100} // return books that have a price greater or equal to 100
// initialize a rest server with a custom base URL
var restServer = new FakeRest.Server('http://my.custom.domain'); // // only URLs starting with my.custom.domain will be intercepted
restServer.toggleLogging(); // logging is off by default, enable it to see network calls in the console
// Set all JSON data at once - only if identifier name is 'id'
restServer.init(json);
// modify the request before FakeRest handles it, using a request interceptor
// request is {
// url: '...',
// headers: [...],
// requestBody: '...',
// json: ..., // parsed JSON body
// queryString: '...',
// params: {...} // parsed query string
// }
restServer.addRequestInterceptor(function(request) {
var start = (request.params._start - 1) || 0;
var end = request.params._end !== undefined ? (request.params._end - 1) : 19;
request.params.range = [start, end];
return request; // always return the modified input
});
// modify the response before FakeRest sends it, using a response interceptor
// response is {
// status: ...,
// headers: [...],
// body: {...}
// }
restServer.addResponseInterceptor(function(response) {
response.body = { data: response.body, status: response.status };
return response; // always return the modified input
});
// set default query, e.g. to force embeds or filters
restServer.setDefaultQuery(function(resourceName) {
if (resourceName == 'authors') return { embed: ['books'] }
if (resourceName == 'books') return { filter: { published: true } }
return {};
})
// enable batch request handler, i.e. allow API clients to query several resourecs into a single request
// see [Facebook's Batch Requests philosophy](https://developers.facebook.com/docs/graph-api/making-multiple-requests) for more details.
restServer.setBatchUrl('/batch');
// you can create more than one fake server to listen to several domains
var restServer2 = new FakeRest.Server('http://my.other.domain');
// Set data collection by collection - allows to customize the identifier name
var authorsCollection = new FakeRest.Collection([], '_id');
authorsCollection.addOne({ first_name: 'Leo', last_name: 'Tolstoi' }); // { _id: 0, first_name: 'Leo', last_name: 'Tolstoi' }
authorsCollection.addOne({ first_name: 'Jane', last_name: 'Austen' }); // { _id: 1, first_name: 'Jane', last_name: 'Austen' }
// collections have autoincremented identifier but accept identifiers already set
authorsCollection.addOne({ _id: 3, first_name: 'Marcel', last_name: 'Proust' }); // { _id: 3, first_name: 'Marcel', last_name: 'Proust' }
restServer2.addCollection('authors', authorsCollection);
// collections are mutable
authorsCollection.updateOne(1, { last_name: 'Doe' }); // { _id: 1, first_name: 'Jane', last_name: 'Doe' }
authorsCollection.removeOne(3); // { _id: 3, first_name: 'Marcel', last_name: 'Proust' }
var server = sinon.fakeServer.create();
server.autoRespond = true;
server.respondWith(restServer.getHandler());
server.respondWith(restServer2.getHandler());
# Install dependencies
make install
# Watch source files and recompile dist/FakeRest.js when anything is modified
make watch
# Run tests
make test
# Build minified version
make build
FakeRest is licensed under the MIT Licence, sponsored by marmelab.