yracnet / vite-plugin-api-routes

Create API routes from path directory like to Nextjs
MIT License
29 stars 4 forks source link

Per-Route Middleware-Functionality #9

Closed PlayWolfYT closed 1 year ago

PlayWolfYT commented 1 year ago

Adds functionality for per-route middlewares. This can be used for authentication and similar.

Added additional route example/src/api/admin/auth/protected.ts (/api/admin/auth/protected) to show how the middlewares can be used for authentication.

Also updated the README, since it contained dead links (example server.js and handler.js files did not exist).

Created two example files for server.ts and handler.ts

Note: I updated the example/src/api/admin/auth/login.ts file to actually have a working login example.

leaftail1880 commented 1 year ago

Why you cant use builtin use method for that?

auth.ts

export function auth(res, res, next) {
  ...
}

protected/admin.ts


export { auth as default } from "./auth.ts";

export function GET(req, res, next) {
  ...
}

(Assuming that in in plugin settings mapper has 'default': 'use', example)

leaftail1880 commented 1 year ago

Usage in real project: https://github.com/leaftail1880/telegram-bot/blob/7e0e32126c8bd17d5fb984959847e04faf9d9c96/src/web/src/api/oc/owner.ts#L3

PlayWolfYT commented 1 year ago

Well you can use the use method, that is correct, but that would only allow for a single middleware to be used. With this PR it would allow for any number of middlewares which can be chained.

For example:

  1. Authentication Middleware to check if the user is signed in
  2. Authorization Middleware to check if the user has the permission to access this route (potentially requesting information from the database)
  3. Request-Body validation (for example with Zod or Yup) etc.

Code-Example:

// Example-File: /api/users.ts
import { authenticationMiddleware } from './authenticationMiddleware';
import { authorizationMiddleware } from './authorizationMiddleware';
import { bodyValidationMiddleware } from './zodBodyValidation';

export const MIDDLEWARES = [
    authenticationMiddleware,
    authorizationMiddleware,
    bodyValidationMiddleware<UserCreateBody>
]

type UserCreateBody {
    username: string;
    password: string;
}
yracnet commented 1 year ago

Ok, at first I also thought about that possibility and in this case I think the best solution is export an array of functions.

const authorizationMiddleware = (req, res, next) => {
  req.authData = {
    message: "Authorization Middleware Security",
  };
  next();
};
const sessionMiddleware = (req, res, next) => {
  req.sessionData = {
    message: "Session Middleware Restore",
  };
  next();
};
const paramObjectMiddleware = (req, res, next) => {
  req.paramObject = {
    message: "ParamObject Middleware, ParamNames to Object",
  };
  next();
};

export default [
  authorizationMiddleware,
  sessionMiddleware,
  paramObjectMiddleware,
];

I this case, we need change the appplyRouters

applyRouters(
  (props) => {
    const { method, route, path, cb } = props;
    if (handler[method]) {
      if(Array.isArray(cb)){
        // for the route we apply the array function as middleware
         handler[method](route, ...cb);
      } else {
         handler[method](route, cb);
      }
    } else {
      console.log("Not Support", method, "for", route, "in", handler);
    }
  }
);

I think it will be the best solution because the POST, GET and others function would inherit this strategy.

@PlayWolfYT If could changes your PR with this definition, I will merge and release the new version

PlayWolfYT commented 1 year ago

Sure thing, will do it on monday as I don't have time on this sunday, but i'll let you know when the changes are ready 👍