How to get domain in middleware, then pass it to components/store before axios calls [Nuxt] - node.js

I want to access the domain name before any axios calls are made. Because the axios calls need the domain URL to make them.
So, I want to get the domain first, then pass it to the store/components so they can load properly.
My understanding is that the domain is held in the req object of the context which is passed to the midddleware.
How would I get it and then pass it to the store and the components?

You can do it like this. Use nuxtServerInit. This function is called on the serverside, and only once if you reload the page. For this you need to go to your store and add this:
//store
actions: {
nuxtServerInit(store, context){
store.commit("setUrl", context.req.originalUrl);
}
}
Well thats all. People also like to destructure the arguments:
actions: {
nuxtServerInit({ commit},{req}){
commit("setUrl", req.originalUrl);
}
}
I am not sure if its originalUrl or just url... or maybe something different.

To expand on lfaruki's answer, you can do it that way. Here is my solution:
store/index.js
export const state = () => ({
domain: '',
});
export const mutations = {
setDomain(state, domain) {
state.domain = domain;
},
};
export const actions = {
nuxtServerInit(store, context) {
store.commit('setDomain', context.req.headers.host);
},
};
export const getters = {
domain: (state) => state.domain,
};
middleware/domain.js
export default function ({ route, store, redirect }) {
console.log('hey look at that', store.getters['domain']);
}
nuxt.config.js
export default {
...
router: {
middleware: 'domain'
},
}

Related

How to get url params in NextJs like: userid/:this_is_a_param

i'm having trouble trying to get url params with nextJs.
so similar to i'd usually do with express i'd like to get a :param from the url
like so
users/:userid/
console.log(req.params.userid)
all i could do is get the "userid" from url like so ?userid=
i'm also using typescript
I assume that you want to get the "userid" path variable from the respective page.
If the path users/[userid].tsx is a page, then it would look like this:
/pages/users/[userid].tsx:
import { useRouter } from 'next/router'
import { NextPage } from 'next'
const UserPage: NextPage = () => {
const router = useRouter()
const { userid } = router.query
// TODO
return <></>
}
export default UserPage
However, if you want to use the route as an API route, then it looks like this:
/pages/users/[userid].ts:
import type { NextApiRequest, NextApiResponse } from 'next'
export default (req: NextApiRequest, res: NextApiResponse) => {
const { userid } = req.query
// TODO
res.status(200).json({ name: `User ID: ${userid}` })
}
It depends on the place that you need this parameters.
For Client side , you have to use useRouter hook.
For getStaticProps and getServerSideProps you have to use context object that is passed to this functions automatically.
For Api Routes , it's exactlly like express js and you can use req.params.userid

res.setHeader() not working as it is supposed to and not returning cookies in Next JS [duplicate]

I have to send current language on endpoint. But getting language from Cookie returns undefined inside getServerSideProps.
export async function getServerSideProps(context) {
const lang = await Cookie.get('next-i18next')
const res = await fetch(`endpoint/${lang}`)
const data = await res.json()
return {
props: { data },
}
}
export default Index;
What is the proper way to get cookie inside getServerSideProps?
You can get the cookies from the req.headers inside getServerSideProps:
export async function getServerSideProps(context) {
const cookies = context.req.headers.cookie;
return {
props: {},
};
}
You could then use the cookie npm package to parse them:
import * as cookie from 'cookie'
export async function getServerSideProps(context) {
const parsedCookies = cookie.parse(context.req.headers.cookie);
return { props: {} }
}
To avoid having to parse the cookies string from context.req.headers.cookie, Next.js also provides the cookies as an object which can be accessed with context.req.cookies.
export async function getServerSideProps(context) {
const lang = context.req.cookies['next-i18next']
// ...
}
From getServerSideProps documentation:
The req in the context passed to getServerSideProps provides built in
middleware that parses the incoming request (req). That middleware is:
req.cookies - An object containing the cookies sent by the request.
Defaults to {}
You can use parseCookies function with cookie package
import cookie from "cookie"
function parseCookies(req){
return cookie.parse(req ? req.headers.cookie || "" : document.cookie);
}
And then get access like that.
export async function getServerSideProps({ req} ) {
const cookies = parseCookies(req);
// And then get element from cookie by name
return {
props: {
jwt: cookies.jwt,
}
}
}
If you are using Axios this is very simple
This will work inside getServerSideProps method. You can't get access to the cookie by using withCredentials because this is on the server.
const { token } = context.req.cookies;
const response = await axios.get('/staff/single', {
headers: { Cookie: `token=${token};` },
});
or try (This will work on the client)
const response = await axios.get('/staff/single', {
headers: { withCredentials: true },
});
how are you doing?
you can use Something like this :
export async function getServerSideProps(context) {
console.log(context.req.cookies)
}
so easy and so beautifuly!

DELETE method requires query parameter instead of path parameter in next.js

I am creating a CRUD in API,
but the delete does not seems to work properly.
I get a response from this
http://localhost:3000/api/admin/categories?id=1
and not from this
http://localhost:3000/api/admin/categories/1
this is the code in next.js:
export default async (req, res) => {
const {
query: { id },
method,
} = req;
switch (method) {
case "DELETE":
return res.status(200).json({
success: true,
id: id,
});
}
in React:
axios.delete(`http://localhost:3000/api/admin/categories/`, {id: 1})
The same situation is also with "PUT" method
Folder Directory:
api
|
---admin
--------categories
--------index.js
If you want to create API Routes such as DELETE, PUT ( they require /id as path parameter), you should create a separate file in that folder.
Like this:
api
|
---admin
--------categories
--------index.js
--------[id].js
And in the [id].js file:
export default function handler(req, res) {
const { id } = req.query;
if (req.method === "DELETE") {
res.end(`Category: ${id}`);
}
}

Assign route dynamically Node/Express

I need dynamically assign a new route but it for some reason refuses to work.
When I send a request in the Postman it just keeps waiting for a response
The whole picture of what I am doing is the following:
I've got a controller with a decorator on one of its methods
#Controller()
export class Test {
#RESTful({
endpoint: '/product/test',
method: 'post',
})
async testMe() {
return {
type: 'hi'
}
}
}
export function RESTful({ endpoint, method, version }: { endpoint: string, version?: string, method: HTTPMethodTypes }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
const originalMethod = descriptor.value
Reflect.defineMetadata(propertyKey, {
endpoint,
method,
propertyKey,
version
}, target)
return originalMethod
}
}
export function Controller() {
return function (constructor: any) {
const methods = Object.getOwnPropertyNames(constructor.prototype)
Container.set(constructor)
for (let action of methods) {
const route: RESTfulRoute = Reflect.getMetadata(action, constructor.prototype)
if (route) {
const version: string = route.version ? `/${route.version}` : '/v1'
Container.get(Express).injectRoute((instance: Application) => {
instance[route.method](`/api${version}${route.endpoint}`, async () => {
return await Reflect.getOwnPropertyDescriptor(constructor, route.propertyKey)
// return await constructor.prototype[route.propertyKey](req, res)
})
})
}
}
}
}
Is it possible to dynamically set the route in the way?
I mainly use GraphQL but sometimes I need RESTful API too. So, I want to solve this by that decorator
In order for the response to finish, there must be a res.end() or res.json(...) or similar. But I cannot see that anywhere in your code.

Localized routes in koa

I'm developing a site with multiple languages. Some routes will therefore also have to be localized and I'm not sure how to do this properly.
I'm using #koa/router for routing.
For this example it's only English and Swedish but the site will handle more languages.
I can setup routes to match words in different languages like
router.get('/(create-account|skapa-konto)/', (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
});
But, I want the English site to only respond to '/sign-up' and send 404 for '/skapa-konto' (and vice versa).
In the real world the route would point to some controller function. So if I set up individual routes for each language I would have to change all localized routes manually should the controller function change in the future. That's something I would like to avoid ;)
Any suggestions?
I ended up solving this by extending the Router like this:
const LocalizedRouter = class extends Router {
/**
* Set up route mapping
* #param {object} options
*/
constructor(options) {
if (!Array.isArray(options.languages)) {
throw new TypeError('Languages must be of type Array');
}
super(options);
this.languages = options.languages;
}
/**
* Router function for GET method
* #param {string | Object<string, string>} RouteCollection
*/
get(routes, func) {
if (typeof(routes) === 'string') {
super.get(routes, func);
return;
}
if (typeof(routes) === 'object') {
for(const key in routes) {
if(!this.languages.includes(key)) {
continue;
}
if(typeof(func) !== 'function') {
throw new TypeError('Middleware must be a function');
}
const checkLanguageAndMount = async (ctx, next) => {
if(ctx.state.lang !== key) {
return next();
}
return func(ctx, next);
};
super.get(routes[key], checkLanguageAndMount);
}
return;
}
throw new TypeError('"Routes" must be a string or an object');
}
};
I can then set up my routes like this:
const myRouter = new LocalizedRouter({
languages: ['en', 'sv']
});
myRouter.get({
'en': '/create-account',
'sv': '/skapa-konto'
}, (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
};
This can probably be cleaned up but it does solve what I wanted to do.
EDIT: Fixed bug that caused 404 if two languages had identical paths
This problem interested me so I created a small github repo with some code. I'll try to explain here:
I created an array with some options:
const localeConfig = [
{
locale: "en",
routes: [
{
path: "/sign-up",
controllers: [enController],
method: "GET",
},
],
prefix: false,
},
{
locale: "se",
routes: [
{
path: "/skapa-konto",
controllers: [seController],
method: "GET",
},
],
prefix: false,
},
];
I then pass this object to a setupRoutes function that basically iterates the array, generating all the routes according to those options.
const setupRoutes = (localeConfig) => {
// Have some check to prevent duplicate routes
localeConfig.forEach((opt) => {
// Adding prefix according to option
const localePrefix = opt.prefix ? `/${opt.locale}` : "";
opt.routes.forEach((route) => {
const path = `${localePrefix}${route.path}`;
router[route.method.toLowerCase()].apply(router, [
path,
...route.controllers,
]);
});
});
};
So, for instance, if you were to change any of the controllers in either language you would only need to update the specific locale object.route.controllers. I imagine you could even have each different locale in a different file to have some modularity.
The github repo is here and I would really like to have you contribute to it if you have any idea on how to improve this.
Cheers!

Resources