{"id":13801553,"url":"https://github.com/hviana/faster","last_synced_at":"2025-05-12T17:26:22.515Z","repository":{"id":62422570,"uuid":"466133463","full_name":"hviana/faster","owner":"hviana","description":"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!","archived":false,"fork":false,"pushed_at":"2024-11-08T12:24:15.000Z","size":98,"stargazers_count":53,"open_issues_count":0,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-31T13:42:36.991Z","etag":null,"topics":["deno","middleware","server"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hviana.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-04T13:27:53.000Z","updated_at":"2025-01-06T04:07:03.000Z","dependencies_parsed_at":"2023-01-29T23:01:16.391Z","dependency_job_id":"15846b10-481d-4993-8bd8-bb2c90e780c3","html_url":"https://github.com/hviana/faster","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hviana%2Ffaster","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hviana%2Ffaster/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hviana%2Ffaster/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hviana%2Ffaster/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hviana","download_url":"https://codeload.github.com/hviana/faster/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238065528,"owners_count":19410618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","middleware","server"],"created_at":"2024-08-04T00:01:24.267Z","updated_at":"2025-05-12T17:26:22.503Z","avatar_url":"https://github.com/hviana.png","language":"TypeScript","funding_links":[],"categories":["Modules"],"sub_categories":["Web framework"],"readme":"# 🚀 **Faster**\n\n\u003e [!IMPORTANT]\\\n\u003e **Please give a star!** ⭐\n\n---\n\n## 🌟 Introduction\n\n**Faster** is a **fast and optimized middleware server** with an incredibly\nsmall codebase (~300 lines), built on top of native HTTP APIs **with no\ndependencies**. It includes a collection of useful middlewares (Some are\nspecific to Deno):\n\n- 📄 **Log file**\n- 🗂️ **Serve static**\n- 🌐 **CORS**\n- 🔐 **Session**\n- ⏱️ **Rate limit**\n- 🛡️ **Token**\n- 📥 **Body parsers**\n- 🔀 **Redirect**\n- 🔌 **Proxy**\n- 📤 **Handle upload**\n\nFully compatible with **Deno Deploy** and other environments. Examples of all\nresources are available in this README. Faster's ideology is simple: all you\nneed is an optimized middleware manager; all other functionality is middleware.\n\n---\n\n## 📚 **Contents**\n\n- [⚡ Benchmarks](#-benchmarks)\n- [🚀 Example](#-example)\n  - [🛣️ Defining Routes](#%EF%B8%8F-defining-routes)\n  - [📨 POST: Read and Return JSON](#-post-read-and-return-json)\n  - [🌐 GET: Return HTML](#-get-return-html)\n  - [🔍 Get URL Params](#-get-url-params)\n  - [🍪 Cookies](#-cookies)\n  - [↩️ Redirect](#%EF%B8%8F-redirect)\n  - [💬 WebSockets](#-websockets)\n- [🛠️ Middlewares](#%EF%B8%8F-middlewares)\n  - [📦 Set Deno KV and Deno KV FS](#-set-deno-kv-and-deno-kv-fs)\n  - [📝 Logger](#-logger)\n  - [📥 Body Parsers (`res` and `req`)](#-body-parsers-res-and-req)\n  - [⏱️ Rate Limit](#%EF%B8%8F-rate-limit)\n  - [🗂️ Serve Static](#%EF%B8%8F-serve-static)\n  - [🌐 Set CORS](#-set-cors)\n  - [🔑 Token](#-token)\n  - [↩️ Redirect Middleware](#%EF%B8%8F-redirect-middleware)\n  - [🔐 Session](#-session)\n  - [🔌 Proxy](#-proxy)\n  - [📤 Upload](#-upload)\n    - [🚀 Upload Usage](#-upload-usage)\n    - [💻 Upload Examples in Frontend and Backend](#-upload-examples-in-frontend-and-backend)\n- [📁 Organizing Routes in Files](#-organizing-routes-in-files)\n- [📦 All Imports](#-all-imports)\n- [🌐 Example Deploy in Ubuntu](#-example-deploy-in-ubuntu)\n  - [🛠️ Create Service](#%EF%B8%8F-create-service)\n  - [🔒 Configure HTTPS](#-configure-https)\n- [💡 See Also: Faster with React](#-see-also-faster-with-react)\n- [👨‍💻 About](#-about)\n\n---\n\n## ⚡ **Benchmarks**\n\nThe middleware is built on top of Deno's native HTTP APIs. See the benchmarks\n(for a 'Hello World' server):\n\n**Machine**: 8 GiB RAM, Intel® Core™ i5-10210U CPU @ 2.11GHz × 4\\\n**Method**: `autocannon -c 100 -d 40 -p 10 localhost:80`\\\n**Environment**: Deno v1.46.3, Ubuntu 24.04 LTS\n\n| Framework  | Version  | Router? | Results                                   |\n| ---------- | :------: | :-----: | ----------------------------------------- |\n| Express    |  4.19.2  |    ✓    | 167k requests in 40.11s, **29 MB** read   |\n| Fastify    |  4.28.1  |    ✓    | 1105k requests in 40.07s, **193 MB** read |\n| Oak        |  17.0.0  |    ✓    | 260k requests in 40.09s, **45 MB** read   |\n| **Faster** | **12.1** |  **✓**  | **1432k requests in 40.17s, 250 MB read** |\n\n\u003e **Note:** In addition to its performance, Faster is a very complete framework\n\u003e considering its middleware collection.\n\n---\n\n## 🚀 **Example**\n\n### 🛣️ **Defining Routes**\n\n- **Static Routes**: `/foo`, `/foo/bar`\n- **Parameterized Routes**:\n  - Simple: `/:title`, `/books/:title`, `/books/:genre/:title`\n  - With Suffix: `/movies/:title.mp4`, `/movies/:title.(mp4|mov)`\n  - Optional Parameters: `/:title?`, `/books/:title?`, `/books/:genre/:title?`\n- **Wildcards**: `*`, `/books/*`, `/books/:genre/*`\n\n---\n\n### 📨 **POST: Read and Return JSON**\n\n```typescript\nimport { req, res, Server } from \"https://deno.land/x/faster/mod.ts\";\n\nconst server = new Server();\n\nserver.post(\n  \"/example_json\",\n  res(\"json\"),\n  req(\"json\"),\n  async (ctx: any, next: any) =\u003e {\n    console.log(ctx.body);\n    ctx.res.body = { msg: \"json response example\" };\n    await next();\n  },\n);\n\nawait server.listen({ port: 80 });\n\n//or with the portable command \"serve\":\nexport default { fetch: server.fetch };\n```\n\n---\n\n### 🌐 **GET: Return HTML**\n\n```typescript\nserver.get(\n  \"/example_html\",\n  res(\"html\"),\n  async (ctx: any, next: any) =\u003e {\n    ctx.res.body = `\n      \u003c!DOCTYPE html\u003e\n      \u003chtml\u003e\n        \u003chead\u003e\n          \u003cmeta charset=\"utf-8\"\u003e\n          \u003ctitle\u003eTitle Example\u003c/title\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n          HTML body example\n        \u003c/body\u003e\n      \u003c/html\u003e\n    `;\n    await next();\n  },\n);\n```\n\n---\n\n### 🔍 **Get URL Params**\n\n```typescript\nserver.get(\n  \"/example_params/:ex1?foo=bar\",\n  async (ctx: any, next: any) =\u003e {\n    console.log(ctx.params.ex1);\n    console.log(ctx.url.searchParams.get(\"foo\")); // Explore the URL (ctx.url) object\n    await next();\n  },\n);\n```\n\n---\n\n### 🍪 **Cookies**\n\n```typescript\nimport {\n  Cookie,\n  deleteCookie,\n  getCookies,\n  getSetCookies,\n  Server,\n  setCookie,\n} from \"https://deno.land/x/faster/mod.ts\"; // Alias to Deno std\n\nserver.get(\n  \"/cookies\",\n  async (ctx: any, next: any) =\u003e {\n    setCookie(ctx.res.headers, { name: \"user_name\", value: \"San\" }); // Explore interface 'Cookie' for more options\n    deleteCookie(ctx.res.headers, \"last_order\");\n    console.log(getCookies(ctx.req.headers));\n    await next();\n  },\n);\n```\n\n---\n\n### ↩️ **Redirect**\n\nUse: `ctx.redirect([status,] \"/my_custom_url_or_path\")`. The default status is\n`302`.\n\n```typescript\nserver.get(\n  \"/redirect_example\",\n  async (ctx: any, next: any) =\u003e {\n    ctx.redirect(303, \"/my_custom_url_or_path\");\n    await next();\n  },\n);\n\nserver.get(\n  \"/redirect_example2\",\n  async (ctx: any, next: any) =\u003e {\n    ctx.redirect(\"/my_custom_url_or_path\");\n    await next();\n  },\n);\n```\n\n---\n\n### 💬 **WebSockets**\n\nBy default, the server will reject WebSocket connections to prevent\nvulnerabilities. To accept connections, use the `acceptOrRejectSocketConn`\nfunction, which should return an ID to retrieve the WebSocket later. If the\nfunction returns `undefined`, `\"\"`, `null`, `0`, etc., the connection will be\nrejected.\n\n**Example:**\n\n```typescript\nserver.acceptOrRejectSocketConn = async (ctx: Context) =\u003e {\n  // Returning undefined, \"\", null, or 0 will reject the connection.\n  return ctx.req.headers.get(\"Host\")!; // Return ID\n};\n```\n\n**Retrieving the Socket by ID:**\n\n```typescript\nserver.openedSockets.get(yourId); // As in the example, ctx.req.headers.get(\"Host\")!\n```\n\n**Receiving WebSocket Events:**\n\n```typescript\nserver.onSocketMessage = async (id: string, socket: WebSocket, event: any) =\u003e {\n  console.log(id);\n  console.log(socket);\n  console.log(event);\n};\n\nserver.onSocketClosed = async (id: string, socket: WebSocket) =\u003e {\n  console.log(id);\n  console.log(socket);\n};\n//... server.onSocketError, server.onSocketOpen\n```\n\n---\n\n## 🛠️ **Middlewares**\n\nThis project has a standard set of middlewares useful for most cases.\n\n### 📦 **Set Deno KV and Deno KV FS**\n\nYou need to launch Deno KV and Deno KV FS as several middlewares depend on it.\n\n```typescript\nconst kv = await Deno.openKv(); // Use your parameters here to launch a custom Deno.Kv\nServer.setKv(kv);\n```\n\nNow, you can globally access instances in `Server.kv` and `Server.kvFs`.\n\n- **Deno KV File System (`Server.kvFs`):** Compatible with Deno Deploy. Saves\n  files in 64KB chunks. You can organize files into directories, control the\n  KB/s rate for saving and reading files, impose rate limits, set user space\n  limits, and limit concurrent operations—useful for controlling\n  uploads/downloads. Utilizes the Web Streams API.\n\nSee more at: [deno_kv_fs](https://github.com/hviana/deno_kv_fs)\n\n---\n\n### 📝 **Logger**\n\n```typescript\nlogger(save: boolean = true, print: boolean = true)\n```\n\n**Initialize Deno KV (if not already done):**\n\n```typescript\nconst kv = await Deno.openKv();\nServer.setKv(kv);\n```\n\n**Usage:**\n\n```typescript\n// You can also use useAtBeginning\nserver.use(logger()); // With default options: save and print are true\n```\n\n**Access Log Data:**\n\n- **Retrieve Logs:** `await FasterLog.get(startMillis, endMillis)`\n- **Delete Logs:** `await FasterLog.delete(startMillis, endMillis)`\n\n---\n\n### 📥 **Body Parsers (`res` and `req`)**\n\n**Example:**\n\n```typescript\nserver.post(\n  \"/example_parsers\",\n  res(\"json\"), // Response parser\n  req(\"json\"), // Request parser\n  async (ctx: any, next: any) =\u003e {\n    console.log(ctx.body); // The original (unparsed) body is in ctx.req.body\n    ctx.res.body = { msg: \"json response example\" };\n    await next();\n  },\n);\n```\n\n**Supported Options:**\n\n- **`req` Parsers:** `\"arrayBuffer\"`, `\"blob\"`, `\"formData\"`, `\"json\"`, `\"text\"`\n- **`res` Parsers:** `\"json\"`, `\"html\"`, `\"javascript\"`\n\n**Custom Parsing Example:**\n\n```typescript\nserver.post(\n  \"/custom_parse\",\n  async (ctx: any, next: any) =\u003e {\n    ctx.res.headers.set(\"Content-Type\", \"application/json\");\n    const data = await customParseBody(ctx.req.body); // Handle ctx.req.body manually\n    ctx.res.body = JSON.stringify({ msg: \"ok\" });\n    await next();\n  },\n);\n```\n\n---\n\n### ⏱️ **Rate Limit**\n\n**Usage:**\n\n```typescript\n// You can also use useAtBeginning\nserver.use(rateLimit());\n```\n\n**Options (with default values):**\n\n```typescript\nrateLimit({\n  attempts: 30,\n  interval: 10,\n  maxTableSize: 100000,\n  id: (ctx: Context) =\u003e ctx.req.headers.get(\"Host\")!,\n});\n```\n\n---\n\n### 🗂️ **Serve Static**\n\n**Example (route must end with `/*`):**\n\n```typescript\nserver.get(\n  \"/pub/*\",\n  serveStatic(\"./pub\"),\n);\n```\n\n---\n\n### 🌐 **Set CORS**\n\n**Example:**\n\n```typescript\nserver.options(\"/example_cors\", setCORS()); // Enable pre-flight request\n\nserver.get(\n  \"/example_cors\",\n  setCORS(),\n  async (ctx, next) =\u003e {\n    await next();\n  },\n);\n```\n\n**Specify Allowed Hosts:**\n\n```typescript\nsetCORS(\"http://my.custom.url:8080\");\n```\n\n---\n\n### 🔑 **Token**\n\nThis middleware is encapsulated in an entire static class. It uses Bearer Token\nand default options with the \"HS256\" algorithm, generating a random secret when\nstarting the application (you can also set a secret manually).\n\n**Usage:**\n\n```typescript\nserver.get(\n  \"/example_verify_token\", // Send token to server in Header =\u003e Authorization: Bearer TOKEN\n  Token.middleware,\n  async (ctx, next) =\u003e {\n    console.log(ctx.extra.tokenPayload);\n    console.log(ctx.extra.token);\n    await next();\n  },\n);\n```\n\n**Generate Token:**\n\n```typescript\nawait Token.generate({ user_id: \"172746\" }, null); // Null for never expire; defaults to \"1h\"\n```\n\n**Set Secret:**\n\n```typescript\nToken.setSecret(\"a3d2r366wgb3dh6yrwzw99kzx2\"); // Do this at the beginning of your application\n```\n\n**Get Token Payload Outside Middleware:**\n\n```typescript\nawait Token.getPayload(\"YOUR_TOKEN_STRING\"); // For example, to get token data from token string in URL parameter\n```\n\n**Set Configurations:**\n\n```typescript\nToken.setConfigs(/* your configurations */);\n```\n\n---\n\n### ↩️ **Redirect Middleware**\n\n**Usage:** `redirect([status,] \"/my_custom_url_or_path\")`. The default status is\n`302`.\n\n**Example:**\n\n```typescript\nserver.get(\n  \"/my_url_1\",\n  redirect(303, \"/my_url_2\"), // Or the full URL\n);\n\nserver.get(\n  \"/my_url_2\",\n  redirect(\"/my_url_3\"), // Or the full URL\n);\n```\n\n---\n\n### 🔐 **Session**\n\n**Initialize Deno KV (if not already done):**\n\n```typescript\nconst kv = await Deno.openKv();\nServer.setKv(kv);\n```\n\n#### **Example**\n\n```typescript\n// You can also use useAtBeginning\nserver.use(session());\n\n// In routes:\nserver.get(\n  \"/session_example\",\n  async (ctx, next) =\u003e {\n    console.log(ctx.extra.session); // Get session data\n    ctx.extra.session.value.foo = \"bar\"; // Set session data (foo =\u003e \"bar\")\n    await next();\n  },\n);\n```\n\n- The default engine uses Deno KV and is optimized.\n\n#### **Expiration Policies**\n\n- **Absolute Expiration:** The object in the cache will expire after a certain\n  time from when it was inserted, regardless of its usage. A value of `0`\n  disables this expiration.\n- **Sliding Expiration:** The object expires after a configured time from the\n  last request (`get` or `set`). A value of `0` disables this expiration.\n\n**Note:** If both `slidingExpiration` and `absoluteExpiration` are `0`,\nexpiration is disabled. If both are greater than `0`, `absoluteExpiration`\ncannot be less than `slidingExpiration`.\n\n**Session Storage Engine Interface:**\n\n```typescript\nconstructor(\n  slidingExpiration: number = 0,\n  absoluteExpiration: number = 0\n)\n```\n\n**Default Values:**\n\n```typescript\nsession(engine: SessionStorageEngine = new KVStorageEngine()) // Default is 60 min slidingExpiration\n```\n\n---\n\n### 🔌 **Proxy**\n\n**Usage:**\n\n```typescript\n// You can also use useAtBeginning\nserver.use(proxy({ url: \"https://my-url-example.com\" }));\nserver.use(proxy({ url: async (ctx) =\u003e \"https://my-url-example.com\" }));\n```\n\n**In Routes:**\n\n```typescript\nserver.get(\n  \"/proxy_example\",\n  async (ctx, next) =\u003e {\n    console.log(ctx.req); // Request points to the proxy\n    console.log(ctx.res); // Response contains the proxy answer\n    await next();\n  },\n);\n```\n\n**Specific Proxy Route:**\n\n```typescript\nserver.get(\n  \"/proxy_example\",\n  proxy({\n    url: \"https://my-url-example.com/proxy_ex2\",\n    replaceProxyPath: false, // Specific proxy route for \"/proxy_example\"\n  }),\n  async (ctx, next) =\u003e {\n    console.log(ctx.req);\n    console.log(ctx.res);\n    await next();\n  },\n);\n```\n\n**Conditional Proxy:**\n\n```typescript\nserver.get(\n  \"/proxy_example\",\n  proxy({\n    url: \"https://my-url-example.com/proxy_ex3\",\n    condition: (ctx) =\u003e {\n      return ctx.url.searchParams.get(\"foo\") ? true : false;\n    },\n  }),\n  async (ctx, next) =\u003e {\n    console.log(ctx.extra.proxied); // True if proxy condition is true\n    console.log(ctx.req);\n    console.log(ctx.res);\n    await next();\n  },\n);\n```\n\n**Options (with default values):**\n\n```typescript\nproxy({\n  url: string,\n  replaceReqAndRes: true,\n  replaceProxyPath: true,\n  condition: (ctx: Context) =\u003e true,\n});\n```\n\n\u003e **Warning:** Do not use \"res body parsers\" with `replaceReqAndRes: true`\n\u003e (default)!\\\n\u003e **Note:** If you don't use Request body information before the proxy or in\n\u003e your condition, avoid using \"req body parsers\" to reduce processing cost.\n\n---\n\n### 📤 **Upload**\n\n**Initialize Deno KV (if not already done):**\n\n```typescript\nconst kv = await Deno.openKv();\nServer.setKv(kv);\n```\n\nThis middleware uses Deno KV File System\n([deno_kv_fs](https://github.com/hviana/deno_kv_fs)).\n\n#### 🚀 **Upload Usage**\n\n**Example:**\n\n```typescript\n// The route must end with *\nserver.post(\"/files/*\", upload(), async (ctx: any, next: any) =\u003e {/* ... */});\nserver.get(\"/files/*\", download(), async (ctx: any, next: any) =\u003e {/* ... */});\n```\n\n**With Custom Options:**\n\n- **Download:**\n\n```typescript\nserver.post(\n  \"/files/*\",\n  upload({\n    allowedExtensions: async (ctx: Context) =\u003e [\"jpg\"],\n    maxSizeBytes: async (ctx: Context) =\u003e\n      (ctx.extra.user.isPremium() ? 1 : 0.1) * 1024 * 1024 * 1024, // 1GB or 100MB\n    maxFileSizeBytes: async (ctx: Context) =\u003e\n      (ctx.extra.user.isPremium() ? 1 : 0.1) * 1024 * 1024 * 1024, // 1GB or 100MB\n    chunksPerSecond: async (ctx: Context) =\u003e\n      (ctx.extra.user.isPremium() ? 10 : 1) /\n      kvFs.getClientReqs(ctx.extra.user.id),\n    maxClientIdConcurrentReqs: async (\n      ctx: Context,\n    ) =\u003e (ctx.extra.user.isPremium() ? 10 : 1),\n    clientId: async (ctx: Context) =\u003e ctx.extra.user.id,\n    validateAccess: async (ctx: Context, path: string[]) =\u003e\n      ctx.extra.user.hasDirAccess(path),\n  }),\n  async (ctx: any, next: any) =\u003e {/* ... */},\n);\n```\n\n- **Upload:**\n\n```typescript\nserver.get(\n  \"/files/*\",\n  download({\n    chunksPerSecond: async (ctx: Context) =\u003e\n      (ctx.extra.user.isPremium() ? 10 : 1) /\n      kvFs.getClientReqs(ctx.extra.user.id),\n    maxClientIdConcurrentReqs: async (\n      ctx: Context,\n    ) =\u003e (ctx.extra.user.isPremium() ? 10 : 1),\n    clientId: async (ctx: Context) =\u003e ctx.extra.user.id,\n    validateAccess: async (ctx: Context, path: string[]) =\u003e\n      ctx.extra.user.hasDirAccess(path),\n    maxDirEntriesPerSecond: async (\n      ctx: Context,\n    ) =\u003e (ctx.extra.user.isPremium() ? 1000 : 100),\n    pagination: async (ctx: Context) =\u003e true,\n    cursor: async (ctx: Context) =\u003e ctx.url.searchParams.get(\"cursor\"),\n  }),\n);\n```\n\n#### 💻 **Upload Examples in Frontend and Backend**\n\n**Frontend (AJAX with multiple files):**\n\n```javascript\nconst files = document.querySelector(\"#yourFormId input[type=file]\").files;\nconst name = document.querySelector(\"#yourFormId input[type=file]\")\n  .getAttribute(\"name\");\n\nconst form = new FormData();\nfor (let i = 0; i \u003c files.length; i++) {\n  form.append(`${name}_${i}`, files[i]);\n}\nconst userId = 1; // Example\nconst res = await fetch(`/files/${userId}`, {\n  method: \"POST\",\n  body: form,\n}).then((response) =\u003e response.json());\n\nconsole.log(res);\n```\n\n**Backend (Deno):**\n\n```typescript\nimport {\n  download,\n  res,\n  Server,\n  upload,\n} from \"https://deno.land/x/faster/mod.ts\";\n\nconst server = new Server();\n\nserver.post(\n  \"/files/*\", // For example: /files/general/myFile.xlsx\n  res(\"json\"),\n  upload(), // Using default options. No controls.\n  async (ctx: any, next: any) =\u003e {\n    ctx.res.body = ctx.extra.uploadedFiles;\n    await next();\n  },\n);\n\nserver.get(\n  \"/files/*\",\n  download(), // Using default options. No controls.\n);\n\nserver.get(\"/\", res(\"html\"), async (ctx: any, next: any) =\u003e {\n  ctx.res.body = `\n    \u003cform id=\"yourFormId\" enctype=\"multipart/form-data\" action=\"/upload\" method=\"post\"\u003e\n      \u003cinput type=\"file\" name=\"file1\" multiple\u003e\u003cbr\u003e\n      \u003cinput type=\"submit\" value=\"Submit\"\u003e\n    \u003c/form\u003e\n  `;\n  await next();\n});\n\nawait server.listen({ port: 80 });\n\n//or with the portable command \"serve\":\nexport default { fetch: server.fetch };\n```\n\n---\n\n## 📁 **Organizing Routes in Files**\n\nIt's possible to organize routes into files using native JavaScript resources.\n\n**Main File:**\n\n```typescript\nimport { Server } from \"https://deno.land/x/faster/mod.ts\";\nimport exampleRoutes from \"./example_routes.ts\";\n\nconst server = new Server();\nexampleRoutes(\"example\", server);\n\nawait server.listen({ port: 80 });\n\n//or with the portable command \"serve\":\nexport default { fetch: server.fetch };\n```\n\n**Secondary Route File (`example_routes.ts`):**\n\n```typescript\nimport { req, res, Server } from \"https://deno.land/x/faster/mod.ts\";\n\nexport default function exampleRoutes(namespace: string, server: Server) {\n  server.post(\n    `${namespace}/json`,\n    res(\"json\"),\n    req(\"json\"),\n    async (ctx: any, next: any) =\u003e {\n      console.log(ctx.body);\n      ctx.res.body = { msg: \"json response example\" };\n      await next();\n    },\n  );\n\n  server.get(\n    `${namespace}/html`,\n    res(\"html\"),\n    async (ctx: any, next: any) =\u003e {\n      ctx.res.body = `\n        \u003c!DOCTYPE html\u003e\n        \u003chtml\u003e\n          \u003chead\u003e\n            \u003cmeta charset=\"utf-8\"\u003e\n            \u003ctitle\u003eTitle Example\u003c/title\u003e\n          \u003c/head\u003e\n          \u003cbody\u003e\n            HTML body example\n          \u003c/body\u003e\n        \u003c/html\u003e\n      `;\n      await next();\n    },\n  );\n}\n```\n\n---\n\n## 📦 **All Imports**\n\n```typescript\nimport {\n  Context,\n  ContextResponse, // Type\n  Cookie, // Type, alias to Deno std\n  deleteCookie, // Alias to Deno std\n  download,\n  FasterLog,\n  getCookies, // Alias to Deno std\n  getSetCookies, // Alias to Deno std\n  KVStorageEngine,\n  logger,\n  NextFunc, // Type\n  Params, // Type\n  parse,\n  ProcessorFunc, // Type\n  proxy,\n  rateLimit,\n  redirect,\n  req,\n  res,\n  Route, // Type\n  RouteFn, // Type\n  Server,\n  serveStatic,\n  Session, // Type\n  session,\n  SessionStorageEngine,\n  setCookie, // Alias to Deno std\n  setCORS,\n  Token,\n  upload,\n} from \"jsr:@hviana/faster\";\nimport * as jose from \"jsr:@hviana/faster/jose\"; // jsr port of deno panva/jose (v6.0.8)\nimport * as deno_kv_fs from \"jsr:@hviana/faster/deno-kv-fs\"; // Alias to jsr @hviana/deno-kv-fs (v1.0.1)\n```\n\n---\n\n## 🌐 **Example Deploy in Ubuntu**\n\nExample of deploying an application named \"my-deno-app\" in a Ubuntu environment.\nChange \"my-deno-app\" and directories to yours.\n\n### 🛠️ **Create Service**\n\n**Create Run Script (\"run-server.sh\") in Your Application Folder:**\n\n```bash\n#!/bin/bash\n/home/ubuntu/.deno/bin/deno run --allow-all --unstable-kv /home/ubuntu/my-deno-app/app.ts\n```\n\n**Give Execution Permission to the Script:**\n\n```bash\nchmod +x run-server.sh\n```\n\n**Create Service Files:**\n\n```bash\nsudo touch /etc/systemd/system/my-deno-app.service\nsudo nano /etc/systemd/system/my-deno-app.service\n```\n\n**In \"my-deno-app.service\" (change \"Description\", \"WorkingDirectory\", and\n\"ExecStart\" to yours):**\n\n```ini\n[Unit]\nDescription=My Deno App\n\n[Service]\nType=simple\nUser=ubuntu\nWorkingDirectory=/home/ubuntu/my-deno-app\nExecStart=/home/ubuntu/my-deno-app/run-server.sh\nTimeoutSec=30\nRestart=always\nRestartSec=1\n\n[Install]\nWantedBy=multi-user.target\n```\n\n**If Your Application Depends on Another Service (e.g., MongoDB):**\n\n```ini\n[Unit]\nDescription=My Deno App\nAfter=mongod.service\n```\n\n**Enable the \"my-deno-app\" Service:**\n\n```bash\nsudo systemctl enable my-deno-app.service\n```\n\n**Start and Stop the \"my-deno-app\" Service:**\n\n```bash\nsudo service my-deno-app stop\nsudo service my-deno-app start\n```\n\n**View Logs:**\n\n```bash\njournalctl -u my-deno-app.service --since=today -e\n```\n\n---\n\n### 🔒 **Configure HTTPS**\n\n**Install Certbot:**\n\n```bash\nsudo apt install certbot\n```\n\n**Generate Certificates (Port 80 Must Be Free):**\n\n```bash\nsudo certbot certonly --standalone\n```\n\n**During Setup:**\n\nWhen prompted:\n\n```\nPlease enter the domain name(s) you would like on your certificate (comma and/or space separated) (Enter 'c' to cancel):\n```\n\nEnter your domains and subdomains, e.g.: `yourdomain.link www.yourdomain.link`\n\n**Run Your Application on HTTPS (Change \"yourdomain.link\" to Your Domain):**\n\n```typescript\nawait server.listen({\n  port: 443,\n  cert: await Deno.readTextFile(\n    \"/etc/letsencrypt/live/yourdomain.link/fullchain.pem\",\n  ),\n  key: await Deno.readTextFile(\n    \"/etc/letsencrypt/live/yourdomain.link/privkey.pem\",\n  ),\n});\n\n//or with the portable command \"serve\":\n//in this case you need to pass arguments such as port and certificate in the command.\nexport default { fetch: server.fetch };\n```\n\n**Set Up Automatic Certificate Renewal:**\n\nThe certificate is valid for a short period. Set up a cron job to renew\nautomatically.\n\n**Edit Root's Crontab:**\n\n```bash\nsudo crontab -e\n```\n\n**Add to the End of the File (to Check and Renew Every 12 Hours):**\n\n```\n0 */12 * * * certbot -q renew --standalone --preferred-challenges=http\n```\n\n**Alternatively, Check Every 7 Days:**\n\n```\n0 0 * * 0 certbot -q renew --standalone --preferred-challenges=http\n```\n\n---\n\n## 💡 **See Also: Faster with React**\n\nCheck out the complete framework with Faster and React:\n\n👉\n[https://github.com/hviana/faster_react](https://github.com/hviana/faster_react)\n\n---\n\n## 👨‍💻 **About**\n\n**Author:** Henrique Emanoel Viana, a Brazilian computer scientist and web\ntechnology enthusiast.\n\n- 📞 **Phone:** +55 (41) 99999-4664\n- 🌐 **Website:**\n  [https://sites.google.com/view/henriqueviana](https://sites.google.com/view/henriqueviana)\n\n\u003e **Improvements and suggestions are welcome!**\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhviana%2Ffaster","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhviana%2Ffaster","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhviana%2Ffaster/lists"}