Giter Club home page Giter Club logo

next-easy-middlewares's Introduction

next-easy-middlewares

next-easy-middlewares is a simple utility for creating path-based middleware in Next.js applications. Simplify multi-middleware management and reduce general boilerplate in few easy steps with that package.

Installation

npm install next-easy-middlewares
pnpm add next-easy-middlewares
bun add next-easy-middlewares

Usage

Basic definition

Code in middleware.ts file:

import { createMiddleware } from 'next-easy-middlewares';
import { type NextRequest, NextResponse } from 'next/server';

const middlewares = {
  // define your middlewares here...
};

// Create middlewares helper
export const middleware = createMiddleware(middlewares);

export const config = {
  /*
   * Match all paths except for:
   * 1. /api/ routes
   * 2. /_next/ (Next.js internals)
   * 3. /_static (inside /public)
   * 4. /_vercel (Vercel internals)
   * 5. Static files (e.g. /favicon.ico, /sitemap.xml, /robots.txt, etc.)
   */
  matcher: ['/((?!api/|_next/|_static|_vercel|[\\w-]+\\.\\w+).*)'],
};

Matcher types

Simple

// ...

const middlewares = {
  // This will match /blog route only
  '/blog': blogMiddleware,
  // This will match /docs route only
  '/docs': docsMiddleware,
};

Path

// ...

const middlewares = {
  // This will match routes starting with /blog/*
  '/blog/:path*': blogMiddleware,
  // This will match routes starting with /docs/*
  '/docs/:path*': docsMiddleware,
};

Dynamic segments

// ...

const middlewares = {
  // This will match /blog/[slug] routes only
  '/blog/[slug]': blogMiddleware,
  // This will match /blog/[slug]/view routes only
  '/blog/[slug]/view': blogViewMiddleware,
};

RegEx

// ...

const middlewares = {
  // This will match any url in /posts that's next segment is number-typed
  // Example: /posts/123, but not /posts/asd
  'regex:^/posts/\\d+$': regexMiddleware,
};

Middlewares defining

Inline

// ...

const middlewares = {
  // This will match /blog route only
  '/blog': async (request: NextRequest) => {
    console.log('Middleware for /blog', request.nextUrl.pathname);
    return NextResponse.next();
  },
};

Reference

// ...

const blogMiddleware = async (request: NextRequest) => {
  console.log('Middleware for /blog', request.nextUrl.pathname);
  return NextResponse.next();
};

const middlewares = {
  // This will match /blog route only
  '/blog': blogMiddleware,
};

Import

Recommended good practice!

import { blogMiddleware } from '@/app/(blog)/_middleware';

// ...

const middlewares = {
  // This will match /blog route only
  '/blog': blogMiddleware,
};

Middleware chaining

This packages can intercept NextResponse.next() returned from middleware function to chain middlewares for same matcher.

// ...

const middlewares = {
  // This will match /blog route only and execute both middlewares for it
  '/blog': [blogMiddleware, blogSecondMiddleware],
};

Global middlewares

You can define global middleware that would be executed in every middleware execution in your application. I've implemented runtime policy, so you can decide if it will be executed before/after (or both) than rest of defined middlewares.

// ...

const globalMiddlewares = {
  before: authMiddleware,
  after: analyticsMiddleware,
};

const middlewares = {
  // define your middlewares here...
};

// Create middlewares helper
export const middleware = createMiddleware(middlewares, globalMiddlewares);

// ...

Motivation

I'm working with Next.js project for a few years now, after Vercel moved multiple /**/_middleware.ts files to a single /middleware.ts file, there was a unfilled gap - but just for now. After a 2023 retro I had found that there is no good solution for that problem, so I took matters into my own hands. I wanted to share that motivation with everyone here, as I think that we all need to remember how it all started.

Hope it will save you some time and would make your project DX better!

next-easy-middlewares's People

Contributors

z4nr34l avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

next-easy-middlewares's Issues

Usage examples

Working examples that will show how it works practically, not theoretically.

  • Basic
  • NextAuth

Middlewares chaining

As suggested on reddit - it would be nice to have option for middleware chaining.

Example usage:

export const middleware = createMiddleware({
  '/blog': [blogMiddleware, blogMiddleware2],
});

Matcher auto generation

Description:
Should generate middleware handler and matcher for exporting in config, where matcher will contain minimal matcher required.

Example:

const {middleware, matcher} = createMiddleware(middlewares, globalMiddlewares);
const config = {
  matcher
};

export { middleware, config }

Chaining Rewrites

Hello!

I have 3 middlewares.

  • Custom subdomain (namespace) -> path middleware (hello.example.com/route -> /hello/route)
  • intlMiddleware /route -> /[locale]/route
  • NextAuth (redirects if not authed)

intl and the custom subdomain both do url rewrites.

The only docs I found around performing a rewrite before or after intl is the second half of this comment.
amannn/next-intl#247 (comment)
And the intl docs here:
https://next-intl-docs.vercel.app/docs/routing/middleware#example-additional-rewrites

It seems with this complex use case I may not be able to leverage this middleware helper?

typing issues with nextauth middleware

Do you have any idea why I'm getting these type errors:

import { createMiddleware } from "next-easy-middlewares";
import {withAuth} from "next-auth/middleware";

const customWithAuth = withAuth({
    pages: {
        signIn: "/login",
        error: "/not-found",
    },
});

const middlewares = {
    '/dashboard:path*': customWithAuth,
    '/ebay-callback': customWithAuth,
    '/ebay-auth': customWithAuth,
    
};

export default createMiddleware(middlewares);

I am getting the below errors:

Argument of type '{ '/dashboard:path*': NextMiddlewareWithAuth; '/ebay-callback': NextMiddlewareWithAuth; '/ebay-auth': NextMiddlewareWithAuth; }' is not assignable to parameter of type 'MiddlewareConfig'.
  Property ''/dashboard:path*'' is incompatible with index signature.
    Type 'NextMiddlewareWithAuth' is not assignable to type 'MiddlewareFunction | MiddlewareFunction[]'.
      Type 'NextMiddlewareWithAuth' is not assignable to type 'MiddlewareFunction'.
        Target signature provides too few arguments. Expected 2 or more, but got 1.

I am trying to use the NextAuth middleware.

Domains matching

I want to add domains matching for SaaS products to use middleware/chains depending on domains.

multi middleware headers not applied, only last one is applied in response

Hi,
it seems only the last middleware header in the array is applied
if i put it this way only csrf header is sent in response

const middlewares = {
   "/": [RatelimitMiddleware, csrfMiddleware],
};

middleware2

and putting this way only rate limit headers are sent

const middlewares = {
   "/": [csrfMiddleware, RatelimitMiddleware],
};

middleware1

here is sample project code

https://codesandbox.io/p/github/riazXrazor/nextjsmultimiddlewaretest/master?file=%2Fsrc%2Fmiddleware.ts&workspaceId=cfdd15f1-e875-4cef-bcb7-6f2812f84658

Chaining does not respect redirection

Hello,
Your library should be part of NextJS. It is easy to use and makes a lot of sense to configure middlewares per route.

However, there is one issue at line 48 (authMiddleware)

The problem is that the code loops all middleware and overrides the result. The type is:

export type MiddlewareFunction = (request: NextRequest) => Promise<NextResponse>;

Thus, every middleware must return something. If you have two middlewares and the first one returns a redirect return NextResponse.redirect(........) the expectation is that the next middleware does not run because the chain is broken with the redirection. The problem is that the redirect is returned and subsequent middleware overrides the redirect:

 for (const middlewareFunction of middlewares) {
          // eslint-disable-next-line no-await-in-loop -- ensuring that function fully executed
          const result = await executeMiddleware(request, middlewareFunction);
          if (result) response = result;
        }

The response that had the redirect does not have the value of the middleware after the one who specified the redirection.

A real-life scenario is if you have an early authentication middleware that might redirect to the authentication service. If the middleware is the first one, and redirects if the cookies/query string does not have the right token, then the expectation is the code redirect. However, the current implementation will move to the next middleware. If this next middleware returns NextResponse.next(); the redirect will never happen.

The issue is the check of if (result) which is odd since the middleware cannot return null/undefined in any case.

I might miss understand a configuration, let me know if I am in the wrong. Otherwise, a change could be to check if the return value is a redirection and stop looping.

Monorepo migration

Need to migrate repository to monorepo, I want to have examples and package in same repository.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.