Building Nuxt sitemap from multiple data sources - javascript

I'm working with Nuxt in SSR mode and want to build out a dynamic sitemap for multiple routes/ data sets.
The issue I'm facing now is that the async/ await function only allows 'data' as the variable. The same function with 'post' as the variable leads to a "map function does not exist"
This is what's in my Nuxt.config.js file
sitemap: {
hostname: "https://example.com",
routes: async () => {
let { data } = await axios.get('https://api.example.com/api/v1/locations');
data = data.data.map((loc) => `/locations/${loc.slug}`);
console.log(data);
let { posts } = await axios.get('https://cms.example.com/wp-json/wp/v2/posts');
posts = posts.map((post) => `/posts/${post.slug}`);
console.log(posts);
data.concat(posts);
return data
},
path: '/sitemap.xml'
}
The resulting output I'm looking for should be formatted like this:
[
'/locations/location-one',
'/locations/location-two',
'/locations/location-three',
'/posts/post-one',
'/posts/post-two',
'/posts/post-three',
]
The error I'm getting:
Cannot read property 'map' of undefined
and it's occuring on this line:
posts = posts.map((post) => `/posts/${post.slug}`)
so it appears to me that it's not accepting 'posts' as a valid variable for its own await function.
That call works fine when the first call is commented out and 'data' is used instead of 'posts'

your destructured response is wrong:
replace:
let { posts } = ...
by:
let { data: posts } = ...
Because Axios always returns a "data" attribute, so you just have to rename it as "posts".

Your array concatenation must be like that:
data.concat(posts);
(see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)
The push() method is only to push one item, but not an array of item.
(see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push)

Related

Can't pass array from react to django

I m trying to get an array from react frontend (stored in local storage) to my view class in django but i'm getting this error:
In console:
GET http://127.0.0.1:8000/api/quiz/multiple/ 500 (Internal Server Error)
Django LOGS:
for quiz in quizzes:
TypeError: 'NoneType' object is not iterable
ERROR:django.server:"GET /api/quiz/multiple/ HTTP/1.1" 500 20064
Here's how i store the data in the LocalStorage:
localStorage.setItem('quizzes', JSON.stringify(quizList));
history.push('/start')
And here's how i get it from local storage and pass it to the django using axios:
export default function QuizPage() {
const [DataState,setDataState] = useState([]);
const storedQuizzes = JSON.parse(localStorage.getItem("quizzes"))
useEffect(() => {
axiosInstance
.get(`quiz/multiple/`, {
quizzes: storedQuizzes
}).then((res) => {
setDataState(res.data);
})
.catch((function (error) {
console.log(error)
}));
}, [setDataState]);
and, finally, that's my django view:
class MultipleQuizView(APIView):
permission_classes = [IsAuthenticated]
def get(self,request):
questionsList = []
quizzes = request.data.get('quizzes')
for quiz in quizzes:
currentQuiz = Quiz.objects.get(url=quiz)
quizSerializer = QuizSerializerForMultipleQuizzes(currentQuiz)
question = Question.objects.filter(quiz__url=quiz)
questionSerializer = QuestionSerializer(question, many=True)
quizSerializerData = quizSerializer.data.copy()
quizSerializerData["questions"]=questionSerializer.data
questionsList.append(quizSerializerData)
if questionsList:
return Response(questionsList)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
I'm pretty sure the problem isn't from my view class because i tested it using Postman and it works without any problem.
EDIT:
I just tryed with postman using this body and it works properly:
https://i.stack.imgur.com/3RJ5A.png
So i need to send data from react like this but i don't know how:
{
"quizzes":["securitate","aparare"]
}
Try changing the second param to axios.get as follows:
axiosInstance
.get(`quiz/multiple/`, {
params: {
quizzes: storedQuizzes
}
}).then(...)
Read more about the properties that the second param supports.
SOLVED!
The problem was that i wrote:
quizzes = request.data('quizzes')
instead of:
quizzes = request.data['quizzes']

Create custom query in Strapi with Mongoose

I'm new to both Strapi and Mongoose, so I apologise if this is a stupid question.
Following the docs (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html) I'm trying to create a custom query in Strapi in which I want to return the whole collection called people sorted by name desc. But when I hit the endpoint I get a 500 error and checking the terminal the error message is CastError: Cast to ObjectId failed for value "alldesc" at path "_id" for model "people".
Here's my code:
services/people.js
module.exports = {
findByNameDesc() {
const result = strapi
.query("people")
.model.find()
.sort({ name: "descending" });
return result.map((entry) => entry.toObject());
},
};
controllers/people.js
module.exports = {
async alldesc(ctx) {
const entities = await strapi.services.people.findByNameDesc(ctx);
return entities.map((entity) =>
sanitizeEntity(entity, { model: strapi.models.people })
);
},
};
config/routes.json
{
"routes": [
...
{
"method": "GET",
"path": "/people/alldesc",
"handler": "people.alldesc",
"config": {
"policies": []
}
}
]
}
What am I doing wrong?
UPDATE: even when removing .sort({ name: "descending" }); from the query, the error is still there, so I'm thinking that maybe there's something wrong in the way I use the service in the controller?
The problem was in routes.json. Basically seems like Strapi doesn't like the slash / so instead of /people/alldesc I tried /people-alldesc and it worked.
Also in the service there's no need for return result.map((entry) => entry.toObject());, that causes anther error, simply doing return result works.

Define response structure in Adonisjs with Middleware

I want to define the response structure of my requests in the simplest way, and the first thing that comes in my mind to do this is a middleware.
My endpoints are returning the response content correctly:
{{base_url}}/users returns a list of users:
{
[
{
"id": 44,
"name": "some name"
[...]
}
]
}
What I want to do (in all requests) is to add the fields status and data (or any other I'd like to add), like this:
{
"status": 200,
"data": [
{
"id": 44,
"name": "some name"
[...]
}
]
}
I've created a middleware that waits for the resolution but I'm not able to get the content nor add some property to it.
[...]
async handle ({request, response}, next) {
await next()
const content = response._lazyBody.content
content.status = response.response.statusCode
}
[...]
I know this will not work but I want something similar to this. I've looked in Adonis docs and forum, but no answers fit to my needs.
Any help will be welcome
You can extend Response By extending the core. The simplest way is to create a file inside start folder and name it hooks.js and copy and paste the content below inside it:
const { hooks } = use('#adonisjs/ignitor')
const Response = use('Adonis/Src/Response')
hooks.after.providersBooted(() => {
Response.macro('customJson', function (status, data) {
this.status(status).json({
status,
data
})
})
})
this piece of code extends the Response module and add customJson method to it which takes two arguments, status and data, and send them back to the client.
And here you can see how to use it:
Route.get('/users', async ({ response }) => {
let status = ''// whatever you want
let data = ''// whatever you want
return response.customJson(status, data)
})

Get a javascript object's methods with http get request?

I have an object constructor in my server file that constructs an object that includes some functions. When I send the object with Express in my server.js file and retrieve it with an axios get request in my app.js file, the object's functions are missing. Why is this? How can I send/get the functions with the object?
I'm using React (I don't think that matters though). The functions allow me to update the object's data. The object is supposed to act as a folder for other sites.
Server.js
const sites = []; //array that holds objects
//this function generates a random ID for the object
const makeID = function () {
return '_' + Math.random().toString(36).substr(2, 9);
};
//Here is my object (site constructor)
const makeSite = (customerInfo={}, parent=undefined, isMain=false, isFile=false, subsites=[]) => {
const site = {
customerInfo,
isMain,
subsites,
isFile,
id: makeID(),
get title() {
if (!this.isMain) {
return `${parent.title}/${this.customerInfo.name}`;
} else {
return this.customerInfo.name;
}
},
addSubsites(subsite_arr) {
this.subsites += subsite_arr;
}
};
return site;
}
//Here is a function that allows me to make a default object and add it to the array of sites
publishSite = (info) => {
const newSite = makeSite(info, undefined, true, false); //calling constructor
newSite.addSubsites([ //default subsites
makeSite({name: 'Scope'}, newSite),
makeSite({name: 'Notes'}, newSite),
makeSite({name: 'Material'}, newSite),
makeSite({name: 'Changes'}, newSite),
])
sites.unshift(newSite);
}
publishSite({name: "RED"}); //adds object to sites array
app.listen(port, () => console.log(`Listening on port ${port}`));
// create a GET route
app.get('/sites', (req, res) => {
res.send(sites); //sends sites array (see top of code)
});
App.js
//function that gets sites array and logs it to console/updates state
async refreshSites() {
const {data} = await Axios.get('/sites');
console.log(data);
this.setState({sites: data})
}
When I run the app, this is logged to the console
[{…}]
0:
customerInfo: {name: "RED"}
id: "_pppxh5zy6"
isFile: false
isMain: true
subsites: (4) [{…}, {…}, {…}, {…}]
title: "RED"
__proto__: Object
length: 1
It has all the info except for the methods, and calling the methods throws an error. It's also worth noting that the 'title' property of the object never changes even when I change the objects customerInfo.name directly. How can I send the object's methods and call them in app.js (such as addSubsites)?
You can't send functions through the http protocol. If you wish to use the same constructor and methods, my tip is, move all this code to a file without any environment (nodejs, browser) references, and use the same file in both places.

MeteorJS: How to get title data via server method by given id array

What is the 'meteor'-way to get a document title by a given ID?
Collection (Articles)
{
'_id' : 'Dn59y87PGhkJXpaiZ',
'title' : 'Sample Article',
'slug' : 'sample-article'
}
client
render() {
const data = [
{ _id: 'Dn59y87PGhkJXpaiZ' },
{ _id: 'kJXpaiZDn59y87PGh' }
{ _id: 'y87PGhkJXpaiZDn59' }
]
return (
<List>
{
data.map(r => {
return <List.Item>r._id</List.Item>
})
}
)
}
With this I will get this output:
<List>
<List.Item>Dn59y87PGhkJXpaiZ</List.Item>
<List.Item>kJXpaiZDn59y87PGh</List.Item>
<List.Item>y87PGhkJXpaiZDn59</List.Item>
</List>
Now I want to display the title instead of the id. So normally I would do
data.map(r => {
const title = Articles.findOne({ _id: r._id }).title
return <List.Item>title</List.Item>
})
But the problem is, that data is a dynamic dataset and I can't/don't want to publish the complete Articles collection. Right now there is no subscription, so I don't get any results for the title.
So I think I have to do a server side call.
Meteor.call('getTitle', r._id, function(err, res) {
console.log(res)
})
But then I'll get the result in the callback function. So how do I get these into the list? Also I want to avoid multiple method calls. I think it would be better to send data and get all titles on server side and then build the list.
If you can/want to use a non async call, don't pass a callback to the Meteor.call() method:
data.map(r => {
const title = Meteor.call('getTitle',r._id);
return <List.Item>title</List.Item>
})
As stated in the docs:
If you do not pass a callback on the server, the method invocation will block until the method is complete. It will eventually return the return value of the method, or it will throw an exception if the method threw an exception.
To fetch and render the data meteor way you have to use the package called react-meteor-data to create createContainer.
For example if you were to use it then you would be able to pass it directly to the component as props.
export default createContainer((props) => {
Meteor.subscribe('questions');
return {
questions: Questions.findOne({_id: props.match.params.id})
};
}, QuestionDo);

Categories