@node-scarlet/http
Version:
Build APIs Instantly 🏋️♂️
122 lines (101 loc) • 5.14 kB
Markdown
# Build APIs Instantly 🏋️♂️
Serve [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages) in seconds with URL routing out of the box.
## Down to business
The *almost* 👏 simplest possible example:
Run the program, and visit `localhost:5000` in your browser to see things in action!
```javascript
const http = require('@node-scarlet/http');
const { GET, POST } = http.methods;
const requests = http.server();
requests.route(GET, '/*', (req, meta) => 'Hello, World!');
requests.route(POST, '/json', (req, meta) => ({ success: true }));
requests.listen(5000);
```
## Routing
`requests.route()` always takes the same 3 parameters:
* **method**: The type of [HTTP Method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) the route will apply to. You can use strings like `"GET"`, or you can reference them from `http.methods` as shown above.
* **urlpattern**: A string that represents which URL paths the route will apply to. The syntax is based on the [url-pattern](https://www.npmjs.com/package/url-pattern) format.
* **handler**: A function that determines how the route should respond to requests that it applies to.
## Handler Functions
Request handler functions use `req` and `meta` to determine how to react to incoming requests. If they return a truthy value, that value will be used to respond.
* For simple responses, you can use a `string` or `object` as a return type
* Use `http.response()` to have richer control over the [status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status), [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers), and [body](https://en.wikipedia.org/wiki/HTTP_message_body).
```javascript
// handlers.js
// string/object shorthand
const saySomething = (req, meta) => 'Hello there!';
const getJson = (req, meta) => { message: 'anybody listening?' };
// full response syntax
const denyAccess = (req, meta) => {
return http.response({
status: 403,
headers: { 'content-type', 'text/html' },
body: '<h1>Access Denied</h1>'
})
}
```
### Handler Arguments
#### `req`
The `req` argument is short for **"request"**, and has the following properties:
* `method`: The http verb (GET, POST, etc..) that the request was made with.
* `headers`: An object containing header names, and their corresponding values.
* `url`: The full url pathname starting after the domain, E.G: `"/products/hats?id=45&limit=1"`.
* `params`: An object representation of dynamic url segments. A request matching the url. pattern `"/products/:type"`, might have a `params` value of `{ type: "hats" }`, or `{ type: "watches" }`.
* `body`: A string representation of the request body.
* `query`: An object representation of querystring values, E.G: `{ id: "45", limit: 1 }`.
#### `meta`
`meta` starts off as an empty object, but is intended to be populated with arbitary data that can be used by other handlers. Some handler won't return a response, but instead set properties of `meta` to be used by downstream handlers. One such example might be a handler that authenticates a requester's identity, and stores their account data on `meta`.
```javascript
// handlers.js
const attachMeta = (req, meta) => {
meta.desire = req.query.emotion || 'love';
}
// Utilize the meta value set by attachMeta()
const emote = (req, meta) => {
return http.response({
status: 200,
headers: {},
body: 'Wilber didn\'t want food. He wanted ' + meta.desire,
})
}
```
## Tying things together
For organization, you'll typically want to define your request handlers in their own module instead of inline.
```javascript
const http = require('@node-scarlet/http');
const { GET, POST } = http.methods;
const handlers = require('./handlers');
const requests = http.server();
requests.route(GET, '/*', handlers.attachMeta);
requests.route(GET, '/greeting', handlers.saySomething);
requests.route(POST, '/secrets', handlers.denyAccess);
requests.route(GET, '/sad/story', handlers.emote);
requests.listen(5000);
```
## Stopping Service
Stop listening for requests with `requests.close()`.
## Ports
Specify which networking port to listen over with `requests.listen()`. If no `port` argument is provided, a random available port will be chosen.
Once listening, `requests.port()` will return the active port.
## Static Files
"Static" refers to any file that you may want to serve to browsers outright, like `.html`, `.css`, or `.js`.
In a production environment, Node should rely on a reverse-proxy like Nginx to serve static files, but for small projects or development environments, you can use something like the following.
Assuming the static file directory is called "public":
```javascript
const { resolve, join } = require('path');
const { createReadStream } = require('fs');
/**
* Create a handler that will attempt to serve static files
* from a given directory path.
*
* USAGE:
* requests.route(GET, '/*', staticFiles(__dirname + "public"));
*/
export const staticFiles = path => {
return async req => {
const filepath = join(resolve(path), req.url);
if (filepath[filepath.length-1] != '/')
return createReadStream(filepath);
}
}
```