@labyrinthos/file-routes
v0.2.4
Published
Lightweight library for using file-based routing in AWS Lambda and Express.
Downloads
4
Maintainers
Readme
File Routes
Lightweight library for using file-based routing in AWS Lambda and Express.
Installation
npm i @labyrinthos/file-routes
Extra Request Properties
The follow props are added to both the Request
object in express and the
event
in lambda.
| Property | Description | | --- | --- | | sourceRoute | The source route that was matched in the request. Will have the same format as the file names (minus the extesions) so that it is the same between both types of deployment.
Route Folder Structure
All routes should be in a single folder that will be passed into the
initialization function. Only files ending with .js
or .mjs
will be imported
and used as routes.
Any files starting with -
will be ignored. Files or folders can take path
params by using [variable name] in the file (or folder) name.
Wildcard routes can be made by putting +
at the end of a parameter route
inside the []
(ex: [path+].mjs
).
Actions can be run in front of routes (think app.use in express) by defining
a {actions}.mjs
(or .js
) in the folder. These actions will be run in
top-down order (so /{actions}.mjs
will go before /wat/{actions.mjs}
).
Actions can send data back early to bail out of a route call the same way a
normal handler function would send a value. An actions file should have a
default export with an array of funtions in the order to be called, and each
function will have the same argument list as the handler functions, so requests
can be modified by an action before being passed to the handler.
Action functions apply to every route in the same folder, and every folder under. To target specific routes with actions, you will need to check the path of a request and ignore any routes you dont want to trigger.
Note: If using
.js
in route file names,"type": "module"
needs to be in thepackage.json
file or the import will fail.
Pathing Note: If a relative path is passed to the function for loading routes, it will be relative to the current working directory (treat it as a file function, not as a specialized import).
Handler Function
The handler functions need to export whichever HTTP verbs they will respond to as objects with specific properties. The HTTP verb export should be all caps (GET/POST/HEAD/etc.).
| Property | Description |
| --- | --- |
| handler | The handler function for the route
| description | A description of the route. Currently only supports basic strings (maybe markdown later?)
| type | The type the handler function returns. Will eventually use for something cool on auto documentation. Probably use the same format as schema.
| schema | Used when validating/masking the returned data. See @axel669/joker
lib for details on schema validation.
both examples will generate handlers for the routes
/thing
,/:userID/info
, and/overlay/:overlayID
.
Handler & Response
file-routes abstracts away the response into a single API for both Lambda and Express, meaning code can be converted from one to the other with minimal effort.
The handler is given 2 arguments:
- The Lambda Event or Express Request
- The file-routes Response function
In order for a handler to return data, it must have a return value from the Response function provided (even in Express).
Response
The Response function has 2 call signatures:
Response(data)
Response(headers, data)
The data is whatever data is going to be return in its raw form (the provided
extra types will convert it for sending).
The headers is an object with the keys in http header
form (i.e. Content-Type
).
In order to set a custom status code for the response, use array access with the
status code as the index (res[404]
, res[204]
, etc). The default status code
is 200, except for the redirects which default to 301.
The default call (no type used) will attempt to use the @axel669/joker
library
to validate and mask the returned data, using the schema
defined on the route.
If no schema is defined and the default call is used it will return an error.
Response types:
.json
: JSON data that isn't validated.text
: plain text response.html
: html text response.raw
: return raw data, using the headers to set the Content-Type.redirect
: redirect, uses the content argument as the url to redirect to.base64
: send base64 encoded data back from a Lambda function
In addition to the response types, the response object has its own set of funcs to perform response formatting tasks for parts that are not the return data.
.setCookie(name, value[, options])
: See cookie.serialize for details on the options available to cookies
Examples
export const GET = {
description: "Just returns some random data.",
schema: {
a: "int",
wat: { text: "string" },
c: { d: "string" },
},
handler: async (event, res) => {
// use validation and masking
return res({
a: 10,
b: 12,
wat: [
{ text: "woah" },
{ text: "first try!" }
],
c: {
d: "E",
f: "g100",
h: ["i", 9, "j"]
}
})
}
}
export const POST = {
handler: (event, res) => {
if (Math.random() < 0.5) {
// json without validation/masking
return res.json(
{ "X-Response": "Custom header!" },
[1, 2, 3, 4]
)
}
// custom error code on text returned
return res.text[404]("Not Found")
}
}
export const PUT = {
handler: (event, res) => {
// redirect
res.redirect("https://google.com")
}
}
Lambda Usage
API Gateway Setup
The APIG route that calls the lambda must use {path+}
at the end so that the
Lambda function can identify which route to look for in the handlers.
Lambda File Structure
lambda
│ index.mjs
│ package.json
├─ node_modules
├─ routes
│ │ thing.mjs
│ ├─ [userID]
│ │ │ -internal.mjs
│ │ │ info.mjs
│ ├─ overlay
│ │ │ [overlayID].js
Lambda Function
import lambdaService from "@labyrinthos/file-routes/lambda"
const service = await lambdaService("routes")
export async function handler(event) {
return await service(event)
}
Express Usage
Express File Structure
project
│ server.mjs
│ package.json
├─ node_modules
├─ routes
│ │ thing.mjs
│ ├─ [userID]
│ │ │ -internal.mjs
│ │ │ info.mjs
│ ├─ overlay
│ │ │ [overlayID].js
server.mjs
The return value from initialization is an express.Router
object.
import express from "express"
import fileRoutes from "@labyrinthos/file-routes/express"
const app = express()
const fileRouter = await fileRoutes("routes")
app.use(fileRouter)
// Optionally put it under a route instead of the root of the routes
app.use("/api", fileRouter)