vertrical / tino Goto Github PK
View Code? Open in Web Editor NEWTiny and functional HTTP server for Deno with local JSON REST API for rapid prototyping.
License: MIT License
Tiny and functional HTTP server for Deno with local JSON REST API for rapid prototyping.
License: MIT License
For responders like jsondb we will need "root" option since after simplifying handleUse function (#63) there's no way to determine if '/api' is custom path or a responder.
Example:
app.any(() => ({ path: "/api", use: jsondb(), root: true }));
This way it's enough to test paths with like .startsWith
method.
It should contain first draft full description.
// http :8000/ping/12
const use = () => ({ resp: "pong" });
app.get(() => ({ path: "/ping/:id", use }));
Result: HTTP/1.1 404 Not Found
POST method for jsondb responder.
Add new entity:
POST /api/laptops
{
brand: "...",
everything: "...",
else: "...",
}
It should also create new entities which are not "plural" (in arrays), and besides objects also primitives in body.
POST /api
{
note: {
text: "...",
date: "20-01-2021",
}
}
Now it should be accessible on: GET /api/note
In jsondb.js it's important to cover following(all) functions:
jsondb
buildResponseBody
handleJson
(core)buildResponse
tryProps
tryRestful
tryDirectLens
checkJsonDb
readJsonDb
Add dry
option:
app.any(() => ({ path: "/api", use: jsondb(true) }));
Notice true
as parameter to jsondb()
. Definition of jsondb
should change to something like:
const jsondb = (
dry = false,
process = processJsonOrContent,
checkFile = checkJsonDb
) => ...
Tests should be updated accordingly (if any).
PATCH method for jsondb responder.
PATCH /api/laptops/1
{
onlyThis:: "props",
}
PATCH should only work on objects. It updates only part of a single resource.
PATCH /api/laptops/1: patches an item in array of items
PATCH /api/laptop: patches an item
On success 200 is return with payload body.
Since we won't need deep access with by index like this:
/api/laptops/0/byindex/shipment/2/byindex/to
There should only be a check if the path ends with byindex
.
Header should be set as for example: Accept: text/html
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept)
Controller API should look like:
const controller = () => ({ resp: "pong", status: 200, type: 'text/html' });
app.get(() => ({ path: "/ping", use: controller }));
If "type" is omitted it's set as "text/plain" but if http_server detects an object or array it should remain "application/json" - as it is currently working.
So "type" is optional like "status" and return type should now be like it is currently but with added "type" as optional: { resp, status?, type? }
.
This mostly makes sense for PUT but should work for POST and PATCH too.
Existing entity:
{ id: 1, text: "str1" }
Request:
PUT /api/entity/1
{ text: "str2" }
Modified entity:
{ id: 1, text: "str2" }
Current behaviour:
{ text: "str2" }
When you try to make a POST request for non-existing path, resource is not created in it's parent but in the root object.
For example:
POST /api/laptops/999
{
"id": 999,
"brand": "apple"
}
Will result in:
{
"response": {
"genres": [
"comedy",
"thriller",
"drama"
],
"laptops": [
{
"id": 123,
"brand": "dell"
},
{
"id": 2321,
"brand": "lenovo"
},
{
"id": 3311,
"brand": "toshiba"
}
],
"color": {
"dark": "blue",
"light": "red"
},
"id": 999,
"brand": "apple"
}
}
We need to run e2e tests against possible endpoints.
Let's sum up what it can be:
/api
- whole json/api/genres
- list/api/genres/0
- single item (with lenses)/api/laptops
- list all/api/laptops/123
- by id/api/laptops/9090909
- not found/api/laptops/0
- by index (lenses)/api/laptops?prop=name
- filter by prop (200 success, empty array if no results)/api/color
- object/api/color/dark
- string/api/color/absbdbd
- not found/api
- payload { name: value}
create record at root level/api/genres
- payload string, but can be object/api/genres/0
- bad request (status 422)/api/laptops
- { obj: name }
create item/api/laptops/123
- status 422/api
- payload { name: value}
create record at root level/api/genres
- payload string, but can be object/api/genres/0
- any (obj|string|number|etc)/api/laptops
- { obj: name }
create item/api/laptops/123
- replace/api
- payload { name: value}
update record at root level/api/genres
- status 400/api/genres/0
- status 400 (not an object)/api/laptops
- 400/api/laptops/123
- update item/api
- status 400/api/genres/0
- success 200/api/laptops
- delete laptops
completely/api/laptops/123
- delete item, 200/api/color
- 200, delete color entity completelyapp.any(() => ({ path: "/myapi", use: jsondb() }));
should replicate /api
endpointapp.get(() => ({ path: "/myapi", use: () => ({ resp: "string", status: 201 }) }));
custom responder with use:
app.get(() => ({ path: "/path", resp: "string" }));
direct result, content-type "text/plain"app.any(() => ({ path: "/path", resp: "string" }));
ANY method, direct result, content-type "text/plain"app.get(() => ({ path: "/path", resp: () => "string" }));
same as aboveapp.get(() => ({ path: "/path", resp: async () => "string" }));
same as aboveapp.get(() => ({ path: "/path", resp: { o: 1 } }));
direct result, content-type "application/json"app.post(() => ({ path: "/path", resp: "string" }));
POST method, direct result, content-type "text/plain"app.post(() => ({ path: "/path", resp: { o: 1 } }));
POST method, direct result, content-type "application/json"app.put(() => ({ path: "/path", resp: () => "string" }));
direct result, content-type "text/plain"app.patch(() => ({ path: "/path", resp: () => "string" }));
direct result, content-type "text/plain"app.delete(() => ({ path: "/path", resp: () => "string" }));
direct result, content-type "text/plain"app.delete(() => ({ path: "/path", resp: () => 200 }));
status 200app.not_found(() => ({ resp: () => "Oops" }));
status 404, content "Oops", when none of the above is requestedAny test above with dry=true shouldn't update db.json but return a result.
Any method except GET shouldn't return any payload on success, just 2xx is enough.
Currently it's not possible to handle multiple HTTP request methods on the same path. For example:
app.get(() => ({ path: "/ping", resp: () => "pong" }));
app.delete(() => ({ path: "/ping", resp: () => "pong" }));
will add only delete
method handler on the path ping
.
http://localhost:8000/api/laptops/0
and
http://localhost:8000/api/laptops/0/byindex
return same response but shouldn't. First one is by id.
Try request: :8000/ping
When:
const use = () => ({ resp: "pong", status: 200 });
app.get(() => ({ path: "/ping", use }));
Getting:
HTTP/1.1 200 OK
content-length: 4
But there is no content type.
When dry run is off and user makes a request on /api/laptops
route (or other route where the target is an array), the response should be:
{
"response": []
}
In the same way, if user makes a request on /api/color
route when dry run is off (or other route where the target is an object), the response should be:
{
"response": {}
}
For primitive types it should be:
{
"response": null
}
Currently, when running mutable requests, only parts of the whole content are returned in the body.
http POST :8000/api/laptops brand=abc
{
"response": {
"brand": "abc"
}
}
We'll rename this project to Tino to give name to something that's both HTTP server WITH fake JSON REST API for prototyping as an out of box responder.
On each POST, PUT, PATCH or DELETE method, file json.db should be updated.
Tests should reflect this.
If there's dry run option set, don't save to the file but only output the result.
Trying to access
localhost:8000/api/laptops/123/brand
Results in error:
error: Uncaught RangeError: Maximum call stack size exceeded
maybeNextItem = U.path([singlePathItem], dataPayload);
PUT (only for update), PATCH, DELETE should only return 200/ok for successful operation.
On create it should return created resource - POST always, PUT only when created (not when updated).
PUT method for jsondb responder.
PUT /api/laptops/1
{
brand: "..."
other: "props",
...
}
If the record doesn't exist create it, otherwise replace it.
Same as with POST method in #3 it should accept all possible values too.
These changes are in denoland/deno#6847
And denoland/deno#6848
Similar file already exists in ./tests
.
Add it to .gitignore.
DELETE :8000/api/laptops?brand=lenovo
Deletes all laptops. Should delete only ones which have "brand" set to "lenovo".
Making this request:
POST :8000/api/laptops
{ "brand": "lg" }
Adds null
to the list:
"laptops": [
{
"id": 123,
"brand": "dell"
},
{
"id": 2321,
"brand": "lenovo"
},
{
"id": 3311,
"brand": "toshiba"
},
null
],
Run:
deno lint --unstable utils.js
Try making it similar to Ramda's function: https://github.com/ramda/ramda/blob/v0.27.0/source/isEmpty.js
A test should be written for following cases:
Currently, if you define a route like:
app.delete(() => ({
path: "/path",
use: () => ({ status: 201 }),
}));
It would be possible to hit it with a DELETE request to /pathf
(notice how both are starting with the word path).
Using CLI program should run as if it's set programatically.
deno run json_server.js --dry=true
Now, by default, we should have running server which supports CRUD operations on db.json file on /api route, same as defining manually:
app.any(() => ({ path: "/api", resp: jsondb() }));
--dry-true
means jsondb is called with dry=true option, jsondb(true)
jsondb()
In first draft custom routes are defined manually like in repl.js file.
// From:
app.get(() => ({ path: "/func", body: () => "return" }));
// To:
app.get(() => ({ path: "/func", resp: () => "return" }));
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.