Faster
A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of native HTTP APIs with no dependencies. It also has a collection of useful middlewares: log file, serve static, CORS, session, rate limit, token, body parsers, redirect, proxy and handle upload. For Deno Deploy and other enviroments!
Install / Use
/learn @hviana/FasterREADME
🚀 Faster
[!IMPORTANT]
Please give a star! ⭐
🌟 Introduction
Faster is a fast and optimized middleware server with an incredibly small codebase (~300 lines), built on top of native HTTP APIs with no dependencies. It includes a collection of useful middlewares (Some are specific to Deno):
- 📄 Log file
- 🗂️ Serve static
- 🌐 CORS
- 🔐 Session
- ⏱️ Rate limit
- 🛡️ Token
- 📥 Body parsers
- 🔀 Redirect
- 🔌 Proxy
- 📤 Handle upload
Fully compatible with Deno Deploy and other environments. Examples of all resources are available in this README. Faster's ideology is simple: all you need is an optimized middleware manager; all other functionality is middleware.
📚 Contents
- ⚡ Benchmarks
- 🚀 Example
- 🛠️ Middlewares
- 📁 Organizing Routes in Files
- 📦 All Imports
- 🌐 Example Deploy in Ubuntu
- 💡 See Also: Faster with React
- 👨💻 About
⚡ Benchmarks
The middleware is built on top of Deno's native HTTP APIs. See the benchmarks (for a 'Hello World' server):
Machine: 8 GiB RAM, Intel® Core™ i5-10210U CPU @ 2.11GHz × 4
Method: autocannon -c 100 -d 40 -p 10 localhost:80
Environment: Deno v1.46.3, Ubuntu 24.04 LTS
| Framework | Version | Router? | Results | | ---------- | :------: | :-----: | ----------------------------------------- | | Express | 4.19.2 | ✓ | 167k requests in 40.11s, 29 MB read | | Fastify | 4.28.1 | ✓ | 1105k requests in 40.07s, 193 MB read | | Oak | 17.0.0 | ✓ | 260k requests in 40.09s, 45 MB read | | Faster | 12.1 | ✓ | 1432k requests in 40.17s, 250 MB read |
Note: In addition to its performance, Faster is a very complete framework considering its middleware collection.
🚀 Example
🛣️ Defining Routes
- Static Routes:
/foo,/foo/bar - Parameterized Routes:
- Simple:
/:title,/books/:title,/books/:genre/:title - With Suffix:
/movies/:title.mp4,/movies/:title.(mp4|mov) - Optional Parameters:
/:title?,/books/:title?,/books/:genre/:title?
- Simple:
- Wildcards:
*,/books/*,/books/:genre/*
📨 POST: Read and Return JSON
import { req, res, Server } from "https://deno.land/x/faster/mod.ts";
const server = new Server();
server.post(
"/example_json",
res("json"),
req("json"),
async (ctx: any, next: any) => {
console.log(ctx.body);
ctx.res.body = { msg: "json response example" };
await next();
},
);
await server.listen({ port: 80 });
//or with the portable command "serve":
export default { fetch: server.fetch };
🌐 GET: Return HTML
server.get(
"/example_html",
res("html"),
async (ctx: any, next: any) => {
ctx.res.body = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Title Example</title>
</head>
<body>
HTML body example
</body>
</html>
`;
await next();
},
);
🔍 Get URL Params
server.get(
"/example_params/:ex1?foo=bar",
async (ctx: any, next: any) => {
console.log(ctx.params.ex1);
console.log(ctx.url.searchParams.get("foo")); // Explore the URL (ctx.url) object
await next();
},
);
🍪 Cookies
import {
Cookie,
deleteCookie,
getCookies,
getSetCookies,
Server,
setCookie,
} from "https://deno.land/x/faster/mod.ts"; // Alias to Deno std
server.get(
"/cookies",
async (ctx: any, next: any) => {
setCookie(ctx.res.headers, { name: "user_name", value: "San" }); // Explore interface 'Cookie' for more options
deleteCookie(ctx.res.headers, "last_order");
console.log(getCookies(ctx.req.headers));
await next();
},
);
↩️ Redirect
Use: ctx.redirect([status,] "/my_custom_url_or_path"). The default status is
302.
server.get(
"/redirect_example",
async (ctx: any, next: any) => {
ctx.redirect(303, "/my_custom_url_or_path");
await next();
},
);
server.get(
"/redirect_example2",
async (ctx: any, next: any) => {
ctx.redirect("/my_custom_url_or_path");
await next();
},
);
💬 WebSockets
By default, the server will reject WebSocket connections to prevent
vulnerabilities. To accept connections, use the acceptOrRejectSocketConn
function, which should return an ID to retrieve the WebSocket later. If the
function returns undefined, "", null, 0, etc., the connection will be
rejected.
Example:
server.acceptOrRejectSocketConn = async (ctx: Context) => {
// Returning undefined, "", null, or 0 will reject the connection.
return ctx.req.headers.get("Host")!; // Return ID
};
Retrieving the Socket by ID:
server.openedSockets.get(yourId); // As in the example, ctx.req.headers.get("Host")!
Receiving WebSocket Events:
server.onSocketMessage = async (id: string, socket: WebSocket, event: any) => {
console.log(id);
console.log(socket);
console.log(event);
};
server.onSocketClosed = async (id: string, socket: WebSocket) => {
console.log(id);
console.log(socket);
};
//... server.onSocketError, server.onSocketOpen
🛠️ Middlewares
This project has a standard set of middlewares useful for most cases.
📦 Set Deno KV and Deno KV FS
You need to launch Deno KV and Deno KV FS as several middlewares depend on it.
const kv = await Deno.openKv(); // Use your parameters here to launch a custom Deno.Kv
Server.setKv(kv);
Now, you can globally access instances in Server.kv and Server.kvFs.
- Deno KV File System (
Server.kvFs): Compatible with Deno Deploy. Saves files in 64KB chunks. You can organize files into directories, control the KB/s rate for saving and reading files, impose rate limits, set user space limits, and limit concurrent operations—useful for controlling uploads/downloads. Utilizes the Web Streams API.
See more at: deno_kv_fs
📝 Logger
logger(save: boolean = true, print: boolean = true)
Initialize Deno KV (if not already done):
const kv = await Deno.openKv();
Server.setKv(kv);
Usage:
// You can also use useAtBeginning
server.use(logger()); // With default options: save and print are true
Access Log Data:
- Retrieve Logs:
await FasterLog.get(startMillis, endMillis) - Delete Logs:
await FasterLog.delete(startMillis, endMillis)
📥 Body Parsers (res and req)
Example:
server.post(
"/example_parsers",
res("json"), // Response parser
req("json"), // Request parser
async (ctx: any, next: any) => {
console.log(ctx.body); // The original (unparsed) body is in ctx.req.body
ctx.res.body = { msg: "json response example" };
await next();
},
);
Supported Options:
reqParsers:"arrayBuffer","blob","formData","json","text"resParsers:"json","html","javascript"
Custom Parsing Example:
server.post(
"/custom_parse",
async (ctx: any, next: any) => {
ctx.res.headers.set("Content-Type", "application/json");
const data = await customParseBody(ctx.req.body); // Handle ctx.req.body manually
ctx.res.body = JSON.stringify({ msg: "ok" });
await next();
},
);
⏱️ Rate Limit
Usage:
// You can also use useAtBeginning
server.use(rateLimit());
Options (with default values):
rateLimit({
attempts: 30,
interval: 10,
maxTableSize: 100000,
id: (ctx: Context) => ctx.req.headers.get("Host")!,
});
🗂️ Serve Static
Example (route must end with /*):
server.get(
"/pub/*",
serveStatic("./pub"),
);
🌐 Set CORS
Example:
server.options("/example_cors", setCORS()); // Enable pre-flight request
server.get(
"/example_cors",
setCORS(),
async (ctx, next) => {
await next();
},
);
Specify Allowed Hosts:
setCORS("http://my.custom.url:8080");
🔑 Token
This middleware is encapsulated in an entire static class. It uses Bearer Token and default options with the "HS256" algorithm, generating a random secret when starting the application (you can also set a secret manually).
Usage:
server.get(
"/example_verify_token", // Send token to server in Header => Authorization: Bearer TOKEN
Token.middleware,
async (ctx, next) => {
console.log(ctx.extra.tokenPayload);
console.log(ctx.extra.token);
await
