Change the request type within the next-connect middleware

Currently, I am utilizing next-connect in conjunction with next.js and typescript. My aim is to develop a middleware that can append additional fields to the request object and deduce the new request type. The following code snippet showcases this:

// multipart middleware
export type NextApiRequestMultipart = NextApiRequest & {
  files: Files;
  fields: Fields;
};
export function multipart(
  config?: Options
) {
  return async (
    req: NextApiRequest,
    res: NextApiResponse,
    next: NextHandler
  ) => {
    const { files, fields } = await parseForm(req, config);
    (req as NextApiRequestMultipart).files = files;
    (req as NextApiRequestMultipart).fields = fields;
    return next();
  };
}
export router().post(
  multipart({ multiples: false }),
  async (req, res) => {
    // To access properties without statically typing the request
    const { files, fields } = req;
  }
);

Access the StackBlitz repository for the code: here

Answer №1

After successfully implementing a sample solution, I wanted to share the code demo with you: modified stack-blitz

Here's a brief description of the approach taken:

In order to achieve this, we utilize a controller router builder that stacks the types additions to the Request object of all middleware.

The ControllerBuilder class allows us to add middleware by stacking them and extracting their types for unified RequestType.

We introduced DecoratedMiddleware as an abstraction over middleware to extract their types and stack them accordingly in ControllerBuilder.

A sample middleware implementation such as AuthMiddleware showcases how additional properties of the request can be stated, ensuring contract adherence.

For usage, we include a sample scenario where multiple middlewares are added in sequence and handled efficiently by the ControllerBuilder.

The Builder pattern is showcased here, indicating immutability and the ability to chain methods for reusability.

Feel free to explore the full details and demo through this link: modified stack-blitz

Answer №2

In my application, I opted for a clever yet straightforward solution where I needed to make just one change to the types. Utilizing a middleware function, I implemented some logic that ensures a specific property exists on the request object once it passes through the middleware. This enabled me to create the router with the request type containing that additional property within the util. While this approach may not be considered generic, it effectively handles more simplistic scenarios.

import { NextApiRequest, NextApiResponse } from 'next'
import { createRouter } from 'next-connect'
import { getAuth } from 'my-auth'

type NextApiRequestAuthed = NextApiRequest & {
  userId: string
}

export function getAuthRouter<ResponseData = any>() {
  const router = createRouter<NextApiRequestAuthed, NextApiResponse<ResponseData>>()
  router.use(async (req, res, next) => {
    const auth = getAuth(req)
    if (auth.userId == null) {
      return res.status(400)
    }
    req.userId = auth.userId
    await next()
  })
  return router
}

This functionality can then be easily implemented as shown below:

type ResponseData = { data: string }
const router = getAuthRouter<ResponseData>()

router.get(async (req, res) => {
  const { userId } = req /* type of req is NextApiRequestAuthed */
  ...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to disregard the "Declaration not found" error while using VS Code with TypeScript

Working with Typescript and Vue Cli in VS Code, I encountered a "definition missing warning" at this particular line: import { setupCalendar, DatePicker } from "v-calendar"; The issue states: Could not find a declaration file for module 'v-calen ...

Difficulty encountered while configuring ngx-lightbox within an Angular module

Greetings! I am relatively new to working with Angular and currently in the process of creating a website that requires lightbox galleries within each component. Upon seeking advice, ngx-lightbox was recommended as a solution for my photo gallery needs. Ho ...

What is the best way to design an interface in TypeScript that accepts a class as a parameter, rather than an instance of the class?

I am looking to develop an interface that can receive an actual class instead of an instance of the class. Here is a sample code snippet: class CheckIfGoNextPage{ localResult; next; constructor(localResult:string, next:string){ this.localResult = ...

Using generic arrow functions for curry operations in TypeScript can lead to type errors

This particular function is designed to split a string into three separate parts. transform<T extends String, N>(arr: T): T { let length = arr.length; const split = (fn: (i: N) => T) => (p: (q: N) => N) => (arg: N) => fn(p(arg)); cons ...

Learn how to retrieve images from the web API at 'https://jsonplaceholder.typicode.com/photos' and showcase them on a webpage using Angular10

Using the API "https://jsonplaceholder.typicode.com/photos", I have access to 5 properties: albumId: 1 id: 1 thumbnailUrl: "https://via.placeholder.com/150/92c952" title: "accusamus beatae ad facilis cum similique qui sunt" url: "https://via.placeh ...

IntellJ Editor encounters Typescript error

Currently engaged in a project using Angular 1.6 and Typescript. Up until recently, there were no compilation errors to be found. However, I am now encountering some peculiar errors. The code remains unchanged and the application is functioning properly. ...

Variations in default export behavior in Webpack when using Typescript versus Javascript

In my React application, I have a page that imports a component from another file. // Code snippet from IconPage.tsx import {AccountBalanceIcon} from './icons'; <AccountBalanceIcon /> // Code snippet from ./icons.ts export { default as A ...

Combining multiple arrays of numbers in Typescript into one aggregate

I am looking to combine multiple number values from different arrays in Typescript. My data model looks like this: export class DataModel { date : string; number : any; } The main class contains an array of DataModels: export class CountryData ...

Reset the input or range value of an ion element whenever it appears on the screen

Whenever the user presses a button on the page, I want my ion-input and ion-range slider to reset their value to the value set by [(ngModel)] when they are loaded on the page. The slider/input fields become visible when the button is pressed. Users have th ...

Utilizing the arr.push() method to replace an existing value within an array with a new object, rather than simply adding a new

Seeking help to dynamically render a list of components that should expand or shrink based on values being added or removed from an array of custom objects. However, facing an issue where pushing a value into the array only replaces the previous value inst ...

Material-UI: Tips for aligning pagination button in the center

My attempt to implement Pagination using Material-UI went well, but now I am struggling to center the arrow buttons and page numbers. I initially tried centering them by wrapping them in a <div style={{textAlign: "center"}}>, however this ...

The pairing of Transpiller and Internet Explorer 8 is like a dynamic

In starting my new project, I am considering using BabelJS. However, there is a significant requirement that must be met: it needs to be compatible with IE8. ISSUE: Babel compiles ES6 to ES5, but the support for ES5 on IE8 is lacking. Are there any alter ...

The element fa-icon is unrecognized within the Reusable Module

Can anyone help me understand why I am encountering this issue with a reusable Module (or what mistake I have made)? ERROR Error: Uncaught (in promise): Error: Template parse errors: 'fa-icon' is not a known element Just to clarify: fa-icon ...

Error message: "Window is not defined in Next.js"

Can anyone help me with this issue I'm experiencing: 'window is not defined' error? useEffect(() => { const handleScroll = () => { if(typeof window !== 'undefined') { // scrolling dete ...

Despite being queried, the new content on Hygraph is still not appearing

Currently, I am in the process of developing my personal portfolio website using NextJS and incorporating a blog feature with hygraph for post storage (which is derived from the default nextjs blog setup). However, I have stumbled upon an unusual issue. Af ...

Angular 10 Reactive Form - Controlling character limit in user input field

I'm currently developing an Angular 10 reactive form and I am looking for a way to restrict the maximum number of characters that a user can input into a specific field. Using the maxLength Validator doesn't prevent users from entering more chara ...

Is it necessary to create a wrapper for Angular Material2 components?

I have multiple angular 5 projects in progress and my team is considering incorporating material design components from https://material.angular.io/. Would it be beneficial to create a wrapper layer to contain the material design components? This would me ...

Evaluating password hashes against plaintext passwords in FaunaDB

I am faced with a challenge in my Fauna database where I have a collection of accounts with passwords encrypted using bCrypt. The dilemma lies in how to locate the correct account when given a password in plain text. Prior to encrypting the passwords, I u ...

Exploring the world of typescript with the power of ts-check

I'm having trouble figuring out how to work with a generic function using TypeScript's new ts-check feature. /** * @type {Reducer<IPoiState, any>} */ const poi = handleActions({ [ADD_BOOKMARK_START]: (state) => { return { ...sta ...

Unraveling a discriminated union

I'm currently working on writing code that can handle generic discriminated unions with a type property. Imagine I have multiple discriminated unions defined as follows: interface IFoo { type: "foo"; foo: number; } interface IBar { type: "bar ...