1 | **Micro2** — Node.js based asynchronous micro-library (WIP)
|
2 |
|
3 | [![NPM Version](https://img.shields.io/npm/v/micro2.svg)](https://www.npmjs.com/package/micro2)
|
4 | [![node](https://img.shields.io/node/v/micro2.svg)](https://nodejs.org)
|
5 | [![Build Status](https://travis-ci.org/rjoydip/micro2.svg?branch=master)](https://travis-ci.org/rjoydip/micro2)
|
6 | [![Build status](https://ci.appveyor.com/api/projects/status/qe5x7i3ift8q7rkv/branch/master?svg=true)](https://ci.appveyor.com/project/rjoydip/micro2/branch/master)
|
7 | [![dependencies Status](https://david-dm.org/rjoydip/micro2/status.svg)](https://david-dm.org/rjoydip/micro2?type=dev)
|
8 | [![devDependencies Status](https://david-dm.org/rjoydip/micro2/dev-status.svg)](https://david-dm.org/rjoydip/micro2?type=dev)
|
9 | [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://img.shields.io/badge/license-MIT-blue.svg)
|
10 | [![install size](https://packagephobia.now.sh/badge?p=micro2)](https://packagephobia.now.sh/result?p=micro2)
|
11 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
12 |
|
13 | ## Features
|
14 |
|
15 | * **Easy**: Designed for usage with `async` and `await`
|
16 | * **Middleware**: Support middleware
|
17 | * **Logging**: It has very low overhead logging method built. [pino](https://www.npmjs.com/package/pino)
|
18 |
|
19 | ## Installation
|
20 |
|
21 | **Important:** Right now `Micro2` not ready for production application. For making `micro2` a goal is to faster moduler application development. Make your application with moduler way.
|
22 |
|
23 | ```bash
|
24 | npm install --save micro2
|
25 | ```
|
26 |
|
27 | ## Usage
|
28 |
|
29 | Create an `index.js` file and export a function that accepts the standard [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) and [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) objects:
|
30 |
|
31 | ```js
|
32 | module.exports = (req, res) => {
|
33 | res.end('Welcome to Micro2')
|
34 | }
|
35 | ```
|
36 |
|
37 | or
|
38 |
|
39 | ```js
|
40 | module.exports = () => 'Welcome to Micro2'
|
41 | ```
|
42 |
|
43 | ### `async` & `await`
|
44 |
|
45 | <p><details>
|
46 | <summary><b>Examples</b></summary>
|
47 | <ul><li><a href="./examples/external-api-call">Fetch external api</a></li></ul>
|
48 | </details></p>
|
49 |
|
50 | Micro2 is built for usage with async/await. You can read more about async / await.
|
51 |
|
52 | ```js
|
53 | const sleep = require('then-sleep')
|
54 |
|
55 | module.exports = async (req, res) => {
|
56 | await sleep(500)
|
57 | return 'Ready!'
|
58 | }
|
59 | ```
|
60 | ### API
|
61 |
|
62 | ##### `send(res, statusCode, data = null)`
|
63 |
|
64 | - Use `require('micro2').send`.
|
65 | - `statusCode` is a `Number` with the HTTP status code, and must always be supplied.
|
66 | - If `data` is supplied it is sent in the response. Different input types are processed appropriately, and `Content-Type` and `Content-Length` are automatically set.
|
67 | - `string`: `data` is written as-is.
|
68 | - If JSON serialization fails (for example, if a cyclical reference is found), a `400` error is thrown.
|
69 |
|
70 |
|
71 | ### Sending a different status code
|
72 |
|
73 | So far we have used `return` to send data to the client. `return 'Hello World'` is the equivalent of `send(res, 200, 'Hello World')`.
|
74 |
|
75 | ```js
|
76 | const {send} = require('micro2')
|
77 |
|
78 | module.exports = async (req, res) => {
|
79 | const statusCode = 400
|
80 | const data = { error: 'Custom error message' }
|
81 |
|
82 | send(res, statusCode, data)
|
83 | }
|
84 | ```
|
85 |
|
86 |
|
87 | ##### `send(res, statusCode, data = null)`
|
88 |
|
89 | - Use `require('micro').send`.
|
90 | - `statusCode` is a `Number` with the HTTP status code, and must always be supplied.
|
91 | - If `data` is supplied it is sent in the response. Different input types are processed appropriately, and `Content-Type` and `Content-Length` are automatically set.
|
92 | - `Stream`: `data` is piped as an `octet-stream`. Note: it is _your_ responsibility to handle the `error` event in this case (usually, simply logging the error and aborting the response is enough).
|
93 | - `Buffer`: `data` is written as an `octet-stream`.
|
94 | - `object`: `data` is serialized as JSON.
|
95 | - `string`: `data` is written as-is.
|
96 | - If JSON serialization fails (for example, if a cyclical reference is found), a `400` error is thrown. See [Error Handling](#error-handling).
|
97 |
|
98 | ### Programmatic use
|
99 |
|
100 | You can use Micro2 programmatically by requiring Micro2 directly:
|
101 |
|
102 | ```js
|
103 | const micro2 = require('micro2')
|
104 | const sleep = require('then-sleep')
|
105 |
|
106 | const server = micro2(async (req, res) => {
|
107 | await sleep(500)
|
108 | return 'Hello world'
|
109 | })
|
110 |
|
111 | server.listen(3000)
|
112 | ```
|
113 |
|
114 | ### Middleware support
|
115 |
|
116 | You can applied middleware with server.
|
117 |
|
118 | ```js
|
119 | server.use((req, res, next) => { // err, req, res
|
120 | log.info('middleware 1')
|
121 | next()
|
122 | })
|
123 | ```
|
124 |
|
125 | ### Logger
|
126 |
|
127 | Micro2 uses [pino](https://www.npmjs.com/package/pino) for logging. You could enable logging conditionaly. `process.env.NODE_ENV`
|
128 |
|
129 | ```js
|
130 | const log = server.log({
|
131 | enabled: true
|
132 | })
|
133 | ```
|
134 |
|
135 | ##### micro2(fn)
|
136 |
|
137 | - This function is exposed as the `default` export.
|
138 | - Use `require('micro2')`.
|
139 | - Returns a [`http.createServer`](https://nodejs.org/dist/latest-v6.x/docs/api/http.html#http_class_http_server) that uses the provided `function` as the request handler.
|
140 | - The supplied function is run with `await`. So it can be `async`.
|
141 |
|
142 | ##### sendError(req, res, error)
|
143 |
|
144 | - Use `require('micro').sendError`.
|
145 | - Used as the default handler for errors thrown.
|
146 | - Automatically sets the status code of the response based on `error.statusCode`.
|
147 | - Sends the `error.message` as the body.
|
148 | - Stacks are printed out with `console.error` and during development (when `NODE_ENV` is set to `'development'`) also sent in responses.
|
149 | - Usually, you don't need to invoke this method yourself, as you can use the [built-in error handling](#error-handling) flow with `throw`.
|
150 |
|
151 | ##### createError(code, msg, orig)
|
152 |
|
153 | - Use `require('micro').createError`.
|
154 | - Creates an error object with a `statusCode`.
|
155 | - Useful for easily throwing errors with HTTP status codes, which are interpreted by the [built-in error handling](#error-handling).
|
156 | - `orig` sets `error.originalError` which identifies the original error (if any).
|
157 |
|
158 | ## Error Handling
|
159 |
|
160 | Micro2 allows you to write robust microservices. This is accomplished primarily by bringing sanity back to error handling and avoiding callback soup.
|
161 |
|
162 | If an error is thrown and not caught by you, the response will automatically be `500`. **Important:** Error stacks will be printed as `error` and during development mode (if the env variable `NODE_ENV` is `'development'`), they will also be included in the responses.
|
163 |
|
164 | If the `Error` object that's thrown contains a `statusCode` property, that's used as the HTTP code to be sent. Let's say you want to write a rate limiting module:
|
165 |
|
166 | ```js
|
167 | const rateLimit = require('my-rate-limit')
|
168 |
|
169 | module.exports = async (req, res) => {
|
170 | await rateLimit(req)
|
171 | // ... your code
|
172 | }
|
173 | ```
|
174 |
|
175 | Alternatively you can create the `Error` object yourself
|
176 |
|
177 | ```js
|
178 | if (tooMany) {
|
179 | const err = new Error('Rate limit exceeded')
|
180 | err.statusCode = 429
|
181 | throw err
|
182 | }
|
183 | ```
|
184 |
|
185 | The nice thing about this model is that the `statusCode` is merely a suggestion. The user can override it:
|
186 |
|
187 | ```js
|
188 | try {
|
189 | await rateLimit(req)
|
190 | } catch (err) {
|
191 | if (429 == err.statusCode) {
|
192 | // perhaps send 500 instead?
|
193 | send(res, 500)
|
194 | }
|
195 | }
|
196 | ```
|
197 |
|
198 | If the error is based on another error that **Micro2** caught, like a `JSON.parse` exception, then `originalError` will point to it. If a generic error is caught, the status will be set to `500`.
|
199 |
|
200 | In order to set up your own error handling mechanism, you can use composition in your handler:
|
201 |
|
202 | ```js
|
203 | const {send} = require('micro2')
|
204 |
|
205 | const handleErrors = fn => async (req, res) => {
|
206 | try {
|
207 | return await fn(req, res)
|
208 | } catch (err) {
|
209 | console.log(err.stack)
|
210 | send(res, 500, 'My custom error!')
|
211 | }
|
212 | }
|
213 |
|
214 | module.exports = handleErrors(async (req, res) => {
|
215 | throw new Error('What happened here?')
|
216 | })
|
217 | ```
|
218 |
|
219 | ## Testing
|
220 |
|
221 | TODO
|
222 |
|
223 | ## Contributing
|
224 |
|
225 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
|
226 |
|
227 | As always, you can run the [Jest](https://jestjs.io) tests using: `npm test` and [ESLint](http://eslint.org) lint using: `npm run lint`
|