0.2.4 • Published 1 year ago

@labyrinthos/file-routes v0.2.4

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

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.

PropertyDescription
sourceRouteThe 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 the package.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.).

PropertyDescription
handlerThe handler function for the route
descriptionA description of the route. Currently only supports basic strings (maybe markdown later?)
typeThe type the handler function returns. Will eventually use for something cool on auto documentation. Probably use the same format as schema.
schemaUsed 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: 1. The Lambda Event or Express Request 2. 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)
0.2.1

1 year ago

0.2.0

1 year ago

0.2.3

1 year ago

0.2.2

1 year ago

0.2.4

1 year ago

0.1.7

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago