Next.js rewrites + custom dynamical header for axios on the server-side - url

How i can do dynamical header for axios on the serve-side? I want to make functionality of cities, without edit nextjs folder structure. Rewrities from nextjs solves my problem, but I can't set the header for axios request functions on the server side. useRouter() hook returns non-proxied path.
// next.config.js
...
async Rewrites() {
return [
{
source: '/new-york/:path*',
destination: '/:path*',
},
]
}
...
Im tried use axios intreception function:
// destination _app.js
export default function AxiosInterceptors() {
...
const router = useRouter();
const asPath = router.asPath; // asPath return not non-proxied path, if i use url /new-york/blogs, here i see /blogs;
apiQr.interceptors.request.use(function (config) {
config.headers['city'] = asPath.includes('/new-york') ? '2' : '1'; // city id
return config;
}, function (error) {
return Promise.reject(error);
});
...
}
Im also tried set headers from NextJS _middleware.js but there is no access to axios requests and the axios interceptor function is not called there.
Where and how can I get a stable variable depending on the entered url on the server side so that I can adjust the axios headers?
I expect to get the proxied url in the axios interceptors instance as I showed above, but I get the proxied path.

Related

Next.js with SSR: How to access cookies to retrieve a JWT and then make a post request with it

Explanation of my problem
So I am currently on a project of my traineeship that requires me to make a small app using Redux to manage the state of the app itself and TanStack Query (a.k.a. React Query v4) to manage asynchronous data from API calls
I am currently using Next.js with Server-Side Rendering (SSR)
And I have a big issue at the moment with one of the pages
So when the user logs in, they're redirected to their profile page, and I want to make a post request with the JSON Web Token stored in cookies to retrieve their info such as their first name and last name
What I did
I used the Next.js function getServerSideProps to make a POST request with the JWT, but because my app uses SSR. I do not have access to cookies and the JWT is stored inside cookies, but I cannot have access to client-side information until the component is fully mounted, also the app crashes when reloading because of that
I used the native React hook useEffect, I declared the variable with the value of the JWT since now I have access to the cookies, but now I cannot use the TanStack Query hook useMutation because you cannot use hooks inside other hooks
(Current implementation) I used a similar approach, but this time declared the JWT variable as undefined along with the constant using the useMutation outside the useEffect and added the JWT variable inside the array of dependencies of the hook, then I retrieved changed the value of the variable to contain the JWT and made a POST request but the post request fails
Here's the code of my current implementation
//React
import { useEffect, useState } from "react";
//Next
import Head from "next/head";
import { NextRouter, useRouter } from "next/router";
//Components
import AccountCard from "#/components/AccountCard/AccountCard";
import Button from "../../components/Button/Button";
//Utils
import { log } from "../../utils/functions/helper-functions";
import { savingsData } from "../../utils/variables/savings-data";
import CookieService from "#/utils/services/cookies.service";
import ApiService from "#/utils/services/api.service";
//Redux
//React-Redux
import { useSelector } from "react-redux";
import { useMutation } from "#tanstack/react-query";
//This is the page of the user
/**
* User page
*
* Route: `/profile/`
* */
export default function Profile(): JSX.Element {
// log({ jsonWebToken });
//We IMPORT the value of the logging state of the user when logging in
const userIsLoggedIn: boolean = useSelector((state: any) => {
return state.isLoggedIn;
});
//We're going to use the router hook to get the current to redirect the user
//if they're not logged in
const router: NextRouter = useRouter();
[...]
let jsonWebToken: string | undefined = undefined;
//We make the POST request
const apiService: ApiService = new ApiService();
const userProfileMutation = useMutation({
mutationFn: (jwt: string) => {
return apiService.postProfile(jwt);
},
onMutate: () => {},
onSuccess: (data, variables) => {
log("SUCCESS, USER INFOS:", data);
},
onError: () => {
log("FAILED TO RETRIEVE USER INFOS");
},
});
//We cannot use the push() method of the router to redirect the user to the sign-in page
//if the user isn't logged in because of the SSR (push is client side only)
useEffect(() => {
//If the user isn't logged in we redirect them to the sign-in page
const userIsNotLoggedIn: boolean = !userIsLoggedIn;
if (userIsNotLoggedIn) {
router.push("/sign-in/");
}
//We recover the jwt inside the browser"s cookies
const cookieCreator: CookieService = new CookieService();
//We initialise it
jsonWebToken = cookieCreator.getCookieByName("jwt")?.value;
log(jsonWebToken);
//#ts-ignore
userProfileMutation.mutate(jsonWebToken);
}, [jsonWebToken]);
return (
<>...</>
);
}
So I'd be very grateful if somebody was able to help me with this issue

NestJS req.body from POST method is empty/undefined when awaiting ArrayBuffer

I am trying to send file from Client (Angular) to the NestJS same way like it is working now with Java-springboot API.
I am using POST method in NestJS but, unfortunatelly I am not able to got any data from the body :
here is the code :
#Post('/uploadExportFile')
uploadAttachment(#Req() req: Request, #Body() attachment: ArrayBuffer): any {
console.log(attachment);
return {};
}
content-type is set in header on Client side, I am not sure if I need to set content-types there ? Content type depends on file mimetype it should be (application/pdf/png/jpeg)..not multiform or what I need to do to achieve that attachment object will not return empty {} .
req.body is undefined
What I need to do with that file is to again change it back to Base64 (in angular it is in Base64) but Java API consumes only byte[] so I need to keep that like it is on FE.
any suggestions what is wrong in this "simple" code?
** EDIT **
====↓ EDIT ↓====
Solution: request.body is undefined is:
NestJS use as default body jsonBody, so in that case you have to override for specific routes that you want to use raw-body, and if raw-body is used insted of jsonBody, then the body from request is not undefined and it contain ArrayBuffer.
What you need to do is something like this;
Create rawBody middleware raw-body.middleware.ts
import { Injectable, NestMiddleware } from '#nestjs/common';
import { Request, Response } from 'express';
import * as bodyParser from 'body-parser';
#Injectable()
export class RawBodyMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: () => any) {
bodyParser.raw({type: '*/*'})(req, res, next);
}
}
app.module.ts
export class AppModule implements NestModule {
public configure(consumer: MiddlewareConsumer): void {
consumer
.apply(RawBodyMiddleware)
.forRoutes({
path: '/uploadExportFile',
method: RequestMethod.POST,
})
.apply(JsonBodyMiddleware)
.forRoutes('*');
}
}
and you need to disable bodyparser in main.ts
const app = await NestFactory.create(AppModule, { bodyParser: false })
in new version of NestJS is introduced new option raw-body but I have no possibility to test that https://docs.nestjs.com/faq/raw-body#raw-body
frist thing send the content-type application/x-www-form-urlencoded
and sure you have add UseInterceptors Like FileInterceptor
you can import FileInterceptor
if you need to get buffer try use file.buffer
import {FileInterceptor} from "#nestjs/platform-express";
#Post('upload')
#UseInterceptors(FileInterceptor('file'))
async upload(#Req() request: RequestWithUser, #UploadedFile() file) {
if (!file) {
throw new HttpException('File is required', HttpStatus.BAD_REQUEST);
}
// you have file
return await this.storageService.upload(file, request.user);
}

Get JWT token in NextJS API

I created a NextJS application integrated with Amazon Cognito. I have a landing page that is using the Amplify Auth API (not the components). Now I need to call an external API to do CRUD operations. What's the best way to do this in NextJS?
I'm thinking I'll create an API in NextJS that will forward the request to the actual external REST API. But my problem is I'm not able to get the JWT Token on the API, since it's a backend code.
A code like this:
Auth.currentSession().then(data => console.log(data.accessToken.jwtToken));
Obviously won't work:
[DEBUG] 20:42.706 AuthClass - Getting current session
[DEBUG] 20:42.706 AuthClass - Failed to get user from user pool
[DEBUG] 20:42.707 AuthClass - Failed to get the current user No current user
(node:20724) UnhandledPromiseRejectionWarning: No current user
How can I get the token in the API?
I have resolved this problem by using the aws-cognito-next library.
Following the documentation from https://www.npmjs.com/package/aws-cognito-next, I have created an auth utility:
import { createGetServerSideAuth, createUseAuth } from "aws-cognito-next";
import pems from "../../pems.json"
// create functions by passing pems
export const getServerSideAuth = createGetServerSideAuth({ pems });
export const useAuth = createUseAuth({ pems });
// reexport functions from aws-cognito-next
export * from "aws-cognito-next";
The pem file was generated by issuing the command (needless to say, you must configure an Amazon Cognito service first):
yarn prepare-pems --region <region> --userPoolId <userPoolId>
And finally, in the NextJs API:
import {getServerSideAuth} from "../../src/utils/AuthUtils"
export default async (req, res) => {
const initialAuth = getServerSideAuth(req)
console.log("initialAuth ", initialAuth)
if (initialAuth) {
res.status(200).json({status: 'success'})
} else {
res.status(400).json({status: 'fail'})
}
}
A simple method is to enable ssrContext in your app and Amplify will provide the user credentials to your api
on the frontend eg _app.tsx (or app.js)
import Amplify, { Auth, API } from "aws-amplify";
import awsconfig from "../src/aws-exports";
Amplify.configure({...awsconfig, ssr: true});
Then in the api you can simply get the currently authenticated cognito user
eg api/myfunction.tsx (or js)
import Amplify, { withSSRContext } from "aws-amplify";
import awsExports from "../../src/aws-exports";
Amplify.configure({ ...awsExports, ssr: true });
/* #returns <CognitoUser>,OR null if not authenticated */
const fetchAuthenticatedUser: any = async (req: NextApiRequest) => {
const { Auth } = withSSRContext({ req });
try {
let user = await Auth.currentAuthenticatedUser();
return user;
} catch (err) {
return null;
}
}

In electron how to send custom header and value for every request?

I use electronjs for building a cross platform desktop application. I would like to send a custom header with a value for every request from electron. Initially in loadURL(), i could use extraHeaders to set the custom header. How to send it in all subsequent requests?
As recommended by the documentation, you should use session object and the method onBeforeSendHeaders:
const { session } = require('electron')
// Modify the user agent for all requests to the following urls.
const filter = {
urls: ['https://*.github.com/*', '*://electron.github.io']
}
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent'
callback({ requestHeaders: details.requestHeaders })
})

In dart httpRequest getstring always error

I established a RESTful server, and I can get a simple string with my Chrome or IE using this URL: "http://localhost:8080/WebService/RESTful/getString"
But when using Dart, it always returns a error message:
"[object XMLHttpRequestProgressEvent]"
From the onError() callback method,
I'm sure that server returns a string with "text/plain" MIME type in Java.
Here is the code:
import 'dart:html';
void main() {
HtmlElement btn = document.body.querySelector("#btn");
btn.onClick.listen(onClick);
}
void onClick(Event v) {
var url = "http://localhost:8080/WebService/RESTful/getString";
HttpRequest.getString(url).then((str) {
window.alert(str.toString());
}, onError: (e) {
window.alert(e);
});
}
Who can help me ?
If you try to fetch resources from another server than the one your page was loaded from, this server needs to return CORS headers otherwise your browser refuses to fetch form this other server.
It depends on your server how this can be configured or added.
See for example
- How do you add CORS headers in Redstone interceptor?
- CORS with Dart, how do I get it to work?

Resources