- Table of Content
- Story
- Big picture, as I said
- How to use
- API
serveStatic
Router()
- Helpers on
Request
- Helpers on
Response
- Build and Test
- Code of Conduct
- Contributor
This is project BaiQi of DeepDigEst Inc., which maybe ⏰ simplify the backend app development workflow.
Coming from C/C++ and java background, our server guys have made lots of discussions about to which logic-glue framework we should adopt and decided to choose node.js, the flighty yet powerful VM.
Since then, we have talked about which language system we should fit into, it's all about easy maintenance and quick training for the new stuff, you know that, C/C++ is very difficult to teach others...
So, we choose javascript, and its supplementaries, the plain js, traceurjs, babeljs and so on. Finally, we made a united agree on that it must be typescript - microsoft's typed javascript. Yes, microsoft is great again cause she said she loves oss and linux? 😊
This server framework is aimed at the goal - "simple, fast, debug-faced and stable". It's used for holding the big application logic layer of our backend system. For now we have done a few awesome features like tasks switching, debug information recording and request-response object initializing.
We stale 🐱 the core point of expressjs for maximum compatibility of js ecosystem.
Big thanks for all oss work.
In the future, we will give our best to get continual improvements. Maybe there should be something like Roadmap or big picture? 😢 No! I hate to write doc, you know, my boss would never pay for doc working, which is the most terrible thing, IMO.
Good luck for this project!
An Incoming HTTP Request - by the simple view point - is consist of below parts:
- HTTP method: GET, PUT, POST or DELETE.
- HTTP path: /api/v1/hello?foo=bar&a=b
- HTTP payload: the body of http request.
The whole of system is intrinsically a pre-config system, and all we need to do is just writing the right configure file according to the app logic. But, you should use the code as config principal instead of using the normal json configuration.
In terminology, we call that step - making the Task Process Tree(TPT)
, and each node or leaf of that tree becomes the Router Node
.
We define that anywhere when you meet a /
symbol in the path string, the layers of TPT should increase one. This is the most important convention we use.
A single router node
has three important properties:
- the
matching path
- used to matching http path, and - the
matching method
- used to matching http method. - the
tasks
- used to processing the matched http request, could be a single task or a chain of tasks.
We think, for app logic developers, the first two parts shall fully qualify a http request and the third part is about how to explain our logic.
The tasks can make different process in terms of the Request
object passed into, or call the next()
function to pass the control to next matching tasks, exactly as the express
framework.🤡
For brevity, whole system consists of routers, and single router consists of matching path
, method
and tasks
.
The most important claim, DO NOT resort the skip-level mounting, in which you mount the non direct descendant. Think like this:
// Do not use this!
router.mount("/api/v1, router_v1");
For a specific http request, system will give you an integrated TPT
. And this TPT
will be the key for future performance monitor, optimization and task testing.
Use npm
to install @sevenryze/server-router
, then import the only exported class.
import { Router } from "@sevenryze/server-router"
// Or commonjs
const { Router } = require("@sevenryze/server-router");
let router = new Router();
The static files serve utility shameless borrowed from node module serve-static
.
See more info: https://github.com/expressjs/serve-static
Router
class, used for initializing the router object.
let router = new Router();
// You can now use router to compose the whole system.
path
: the path mounted by sub-router.router
: the sub-router.
Mount different Router
hierarchically to compose the whole response chain.
router_root.mount("/v1", router_v1);
router_v1.mount("/user", router_user);
task: (request, response, next, share) => void
: The task function will get three parameters -request
,response
,next
andshare
, corresponding the Request, Respond of http request; the control passing functionnext()
and the app context share object respectively.
Register the task processing function into router. We support a few common http method
:
GET
request -get()
.POST
request -post()
.PUT
request -put()
.DELETE
request -delete()
.- Match all methods on the same router -
all
. - Match all methods on the same and descendant routers -
common()
.
Note: Generally, on every place where you need to call the next()
to pass the control, you should use the form return next();
to eliminate the side-effect.
Want to know why? see my blog, if there are... 🤡
// sync task
router.get((request, response, next, share) => {
// Do something.
response.send();
return next();
});
// async task
router.get(async (request, response, next, share) => {
// Do something.
return next();
})
- port: The port listening to.
Listen the specific port.
If there are under tests case or would like to get a temp port, you should use number 0
as the argument.
If port is omitted or is 0, the operating system will assign an arbitrary unused port, which can be retrieved by using Router.getListeningAddress().port after the 'listening' event has been emitted.
router.listen(7777);
Close the server.
router.close();
Get the bounding server address info.
The underlying http request.
The underlying http response.
Original, unprocessed request url.
The parsed http url, see https://nodejs.org/dist/latest-v9.x/docs/api/url.html#url_url_strings_and_url_objects for more info.
Point to the accompanied Response object.
The request http method.
The http headers parsed into object format.
The tasks waiting for this request.
Get the client ip and be able to handle behind proxy case.
request.ip;
// => "127.0.0.1"
Points to the accompanied request object.
The underlying http request.
The underlying http response.
object: Headers
- Object used to set the headers, such as { Accept: "text/plain", "X-API-Key": "dde" }.
Set header key
to its value
. If the Content-Type
field is going to be set, this method will automatically turn the value to extensional form, eg."html" to the standard mime forms "text/html", and add the charset if it can be matched in mime-db package.
Return the this object, aka. Respond to make chain-able calls available.
response.setHeader({ Accept: "text/plain", "X-API-Key": "xmt" });
// => Accept: "text/plain"
// => X-API-Key: "xmt"
response.setHeader({ "Content-Type": "json" });
// => Content-Type: "application/json; charset=utf-8"
response.setHeader({ "Content-Type": "html" });
// => Content-Type: "text/html; charset=utf-8"
response.setHeader({ "Content-Type": "bin" });
// => Content-Type: "application/octet-stream"
code: number
- Http status code number such as "404"
Set the status code
of the response.
Return this object for chain-able calls.
response.setStatus(404);
body: string | object | buffer
- Can be a string such as"some string"
, an object such as{some: "haha"}
and a buffer such asnew Buffer("some buffer")
.
Send response to the remote client, and this method will terminate the underlying socket session.
response.send(new Buffer("some buffer"));
response.send({ some: "json" });
response.send("<p>some html</p>");
Use typescript to compile. For more information, you should check the tsconfig.ts file.
npm run production
We use a well-known combination test tool - jest
by facebook. You are to use npm test script to test all, like:
npm test
And if you need to generate test coverage, use:
npm run test-with-coverage
- You are not rambo. Remember that, together, we are a team.
- Please invite someone to review your code, again, you are not immortal.
- Never blame other workmate for the quality of code set, you should know that boss already fined the poor guy.
- Seven Ryze - XmT Inc.
- Cris - XmT Inc.
- Becky - XmT Inc.
Rangeragent - XmT Inc.