Redirect an array of URLs in Express JS - reactjs

I want to assign an array of URLs from a React app (using Express server side rendering) and assign 301 redirects to each URL.
The slug/id format is changing and I need a way to ensure old links already indexed by Google our saved in the wild have a way to find their way to the new URL format.
This function will be a one-time use, as new URLs will follow the new slug/id format, but the redirects need to remain so that they can continue their redirects.
I know how to assign a single redirect to one URL using res.redirect but not an entire array.
(This is my first SO post. Tried to explain this as well as I can but apologies if there are gaps).

You can use a loop to create router.get functions, so something like:
const urls = ['/url1', '/url2', '/url3'];
urls.forEach(url => {
router.get(url, (req, res, next) => {
res.redirect(301, '/redirected-route');
})
)};
If they all need to go to different URLs rather than all to the same one, then you can define the redirect array as an array of objects with oldUrl, and newUrl properties to create a more dynamic redirect.
const urls = [
{ oldUrl: '/url1', newUrl: '/new-url1' },
{ oldUrl: '/url2', newUrl: '/new-url2' },
{ oldUrl: '/url3', newUrl: '/new-url3' }
];
urls.forEach(url => {
router.get(url.oldUrl, (req, res, next) => {
res.redirect(301, url.newUrl);
})
)};

Related

How to have dynamic redirect URLs in Next.js?

I'm working on a Next.js/Django project, which the user is able to add some redirect logic from the admin panel like:
[
{ source: "/about", destination: "google.com" },
{ source: "/about1", destination: "google1.com" },
{ source: "/about2", destination: "google2.com" },
]
and the web application should be able to handle these dynamic redirects.
As the Nextjs docs says, we can do this in next.config.js. The problem is that we can't have dynamic data in next.config.js. With every change in this file, server must be restarted.
Here we need a logic that gets the urls using an API on website load, loops through them and listens for every route calls to see if they match the redirect data or not.
I have tried some other ways too, like trying to use useEffect, but this way causes the website to render 404 page first and then it redirects to the desired url, which is not that nice for user experience viewpoints.
You can use Next.js Middleware to fetch the dynamic redirects array from the API, and add your own logic to handle the redirection.
Unlike redirects in the next.config.js that run at build time, Next.js Middleware runs on each incoming request to your pages and is able to dynamically fetch the redirects every time.
export async function middleware(req) {
// Fetch redirects array from API
const res = await fetch('https://example.com/api/redirects');
const redirects = await res.json();
/* Assuming the returned `redirects` array would have the following structure:
[
{ source: '/about-us', destination: '/about' },
{ source: '/google', destination: 'https://google.com' }
]
*/
// Find the matching redirect object
const redirect = redirects.find((redirect) => req.nextUrl.pathname === redirect.source);
if (redirect) {
if (redirect.destination.startsWith('http')) {
// Handle external URL redirects
return NextResponse.redirect(new URL(redirect.destination));
}
// Handle internal URL redirects
return NextResponse.redirect(new URL(redirect.destination, req.url));
}
// Continue if no matching redirect is found
return NextResponse.next();
}

Nextjs + expressjs + Azure Web App : two factor authentication with express ('fs' can't be used on client side)

Stack : next.js/express.js/typescript/nodemon
I have a dependency on azure devops api which seems to be using 'fs' under the hood. So I can't use this library in any of the pages (including in getInitialProps).
I created a custom route (call it "get_data") in express server which provides me with the data. I call this route in getInitialProps of the page that will render the data (call it data.tsx) .
The whole app is behind two factor authentication in azure web apps. when get_data call is made in getInitialProps, I get a 401. Note that the origin is the same for serving the page and the get_data api.
Is there a way to pass current auth context when I make the get_data api call ?
In the express api, the method currently looks like this :
server.get('/get_data', (_req, res) => {
let ado = new azure_devops() // this uses the azure-devop-api package
ado.get_data().then((data) => {
res.setHeader('Content-Type', 'application/json');
res.json(data) // return data as json
})
});
Is there a way to merge the two (page and data serving) like the following (so I don't have to make extra api call with auth setup) ?
server.get('/data', (req, res) => { //note the change in route that serves the page data.tsx
const actualPage = '/data';
let ado = new azure_devops() // this uses the azure-devop-api package
ado.get_data().then((data) => {
res.setHeader('Content-Type', 'application/json');
res.write(data) // BUT this is where method returns instead i want to just add to the response body
app.render(req, res, actualPage); // THIS is not executed (so the page doesn't get rendered)
})
});
Appreciate any pointers.
Got the answer from this question.
Still making the API request from getInitialProps. I was missing adding cookie from the context.
const res = await fetch('http://' + context.req.headers.host + '/get_data', {
headers: {
cookie: context.req.headers.cookie, // WAS MISSING THIS
}
});

Nextjs How to map multiple routes to one page?

I have a page called blog in my nextjs version 9.
Next create a route /blog for my page. Now I want to have these routes to be mapped to the same blog page and consequently the blog component:
1. /blog/cat1/gold
2. /blog/cat2/silver
3. /blog/cat3/bronze
Actually, I want to use them as query parameters in the route but I don't want to follow this format:
1. /blog?cat=cat1&color=gold
2. /blog?cat=cat2&color=silver
3. /blog?cat=cat3&color=bronze
I used next/router asPath but it is completely client-side and by reloading it returns 404!
In nextjs 9 dynamic routing has provided which may help me but the thing is I want to have only one component and different routes.
Do you have any ideas?
You didn't specify your web server, so I will give an example using expressjs.
const app = next({ dev })
app.prepare().then(() => {
const server = express();
server.get("/blog/:cat/:color", (req, res) => app.render(req, res, `/blog`));
})
If you need to access the value in the page, you can use getInitialProps to get the value by accessing req.params.cat and req.params.color.
Another way is by passing the value directly when calling app.render function:
app.render(req, res, '/posts', { cat: req.params.cat, color: req.params.color })
Here is the example.
You can do this with Next's dynamic routers. Just create a folder for blog-posts and add a dynamic page under it like this:
/pages/blog/[...slug].tsx
Now on your [...slug].tsx will catch all paths, no matter in what depth. And you can catch the path params from the router's query -field. Try this with any path under /blog/**/* -path and it will list path elements:
const Blog = () => {
const {
query: { slug }
} = useRouter();
return (
<ul>
{query.slug.map(value => <li key={value}>{value}</li>)}
</ul>
);
}

how can i add multiple images and preview them using react and express?

im trying to add multiple images using react app and send them to backend code to store them in mongodb
here is the code for the backend :
link
and this is the frontend link
so this code works for just one image
i need to be able to add multiple images
Server
Since you are using multer, change the upload.single() function to upload.array().
For example:
app.post("/addItem",
upload.array('product-image', 4), // 'product-image' is field name and 4 is the max number of files allowed
(req, res) => {
console.log(req.files);
// ... rest of the logic
}
)
Check out docs for upload.array()
Client
Change current <input> to allow multiple files:
<input type="file" name="product-image" onChange={this.fileChangeHandler} multiple>
Now save all the images user picked not only the event.target.files[0]:
fileChangeHandler(event) {
let files = event.target.files
this.setState({ selectedFiles: files })
}
Now add them in FormData and upload as usual:
let formData = new FormData()
formData.append("product-image", this.state.selectedFiles)
That's it! Hope it helps.
PS: I don't think files should be added to state. You can simply add them to a class variable. In this answer I explained why and how to do that.
Update:
You need to loop over the files now. Your /addItem endpoint's code will look something like this:
app.post("/addItem", upload.array('product-image', 4), (req, res) => {
console.log(req.files);
let paths = [];
req.files.forEach(file => {
console.log("new file location", file.path)
let extension = file.originalname.split(".").pop()
fs.rename(file.path, file.path + "." + extension, () => {})
paths.push("/" + file.filename + "." + extension);
});
console.log("body", req.body)
let itemToStore = {
paths: paths, // notice this `paths` now, it was `path`
description: req.body.description
}
console.log("we are adding", itemToStore)
itemData.push(itemToStore)
console.log("updated itemData:", itemData)
res.send(JSON.stringify(itemData))
})
I didn't modify your code, just added a loop. Your 'path' of undefined error should go away.

Concatenated get requests API call in react

This get request - http://localhost:7000/api/search/gender/Female results in
{
"id": 32,
"email": "no"
},
Backend code:
const app = require('express').Router(),
db = require('../../../config/db'),
User = require('../../../config/User')
app.get('/search/:type/:value', async (req, res) => {
const { type, value } = req.params
module.exports = app
What you want is to set up your back end to read query string params and use those params to do your db filtering so you can make a single request to get whatever filtered data you need
Then your url would look something like:
/api/search?email=true&gender=female&city=athens
or whatever combination of params you need at the time
You should be able to pass objects to axios and it will serialze the front end params for you or use the URL API and specifically it's searchParams to construct url's

Resources