NextJs - Additional keys were returned from getStaticPaths - javascript

I'm working on a client's project with NextJs,
On the blog section we have different paths, blog/[:category], blog/[:category]/[:post] and blog/author/[:author] And to get this done I'm using getStaticPaths and getStaticProps.
I first fetch all the posts and authors from ContentfulAPI and then loop into them to create a valid path to pass it into the paths array
Ps: It works when I hard code every path individually..
thats my function:
export const getStaticPaths = async () => {
const posts = await DataController.getEntriesByContentType(
"componentBlog",
);
const blogPosts = posts.items.map(item => {
return {params: {blog_post: [item.fields.category.replace(/\s+/g, '-').replace(/'/g, '').toLowerCase(), item.fields.slug]}}
})
const authors = await DataController.getEntriesByContentType(
"author",
);
const authorPaths = authors.items.map(item => {
return {params: {blog_post: ['author', item.fields.slug]}}
})
return {
paths: [
blogPosts,
authorPaths,
],
fallback: false,
}
}
And i get this error when I try to access a blog link :
error - Error: Additional keys were returned from `getStaticPaths` in page "/blog/[...blog_post]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { blog_post: ... } }
Keys that need to be moved: 0, 1, 2, 3, 4, 5, 6, 7, 8.
at C:\Workspace\phoenix-v2\next\new-phoenix\node_modules\next\dist\build\utils.js:518:23
at Array.forEach (<anonymous>)
at Object.buildStaticPaths (C:\Workspace\phoenix-v2\next\new-phoenix\node_modules\next\dist\build\utils.js:492:17) at processTicksAndRejections (internal/process/task_queues.js:95:5) {
type: 'Error',
page: '/blog/[...blog_post]'
}
I'm not sure why I run into this error..
thank you for helping!

You're currently passing an array with the params, use the spread operator (...) to reduce it. Docs
return {
paths: [...blogPosts, ...authorPaths],
...
};

Related

How to wrap Sequelize result in another object - Adding parent "params" for Nextjs getStaticPaths()

I am trying to fulfill the getStaticPaths of NextJS that demand records to be formatted as:
Which demand the format like so:
[
{
params: {
id: recordId
}
}
]
I can achieve this result with:
const res = await myModel.findAll({
raw: true,
attributes: ['id'],
});
const fixedFormat = res.map(singleRes => {
return {
params: {
id: singleRes.id,
},
};
});
But I was wondering if there's a way that doesn't involve calling map() on such a big array.

Apollo GraphQL merge cached data

I have a page that consists of 2 components and each of them has its own request for data
for example
<MovieInfo movieId={queryParamsId}/>
const GET_MOVIE_INFO = `gql
query($id: String!){
movie(id: $id){
name
description
}
}`
Next component
<MovieActors movieId={queryParamsId}/>
const GET_MOVIE_ACTORS = `gql
query($id: String!){
movie(id: $id){
actors
}
}`
For each of these queries I use apollo hook
const { data, loading, error } = useQuery(GET_DATA, {variable: {id: queryParamsId}}))
Everything is fine, but I got a warning message:
Cache data may be lost when replacing the movie field of a Query object.
To address this problem (which is not a bug in Apollo Client), either ensure all objects of type Movie have IDs, or define a custom merge function for the Query.movie field, so InMemoryCache can safely merge these objects: { ... }
It's works ok with google chrome, but this error affects Safari browser. Everything is crushing. I'm 100% sure it's because of this warning message. On the first request, I set Movie data in the cache, on the second request to the same query I just replace old data with new, so previous cached data is undefined. How can I resolve this problem?
Here is the same solution mentioned by Thomas but a bit shorter
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
YOUR_FIELD: {
// shorthand
merge: true,
},
},
},
},
});
This is same as the following
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
YOUR_FIELD: {
merge(existing, incoming, { mergeObjects }) {
return mergeObjects(existing, incoming);
},
},
},
},
},
});
Solved!
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
YOUR_FIELD: {
merge(existing = [], incoming: any) {
return { ...existing, ...incoming };
// this part of code is depends what you actually need to do, in my
case i had to save my incoming data as single object in cache
}
}
}
}
}
})
});
The other answers still work, but as of Apollo Client >= 3.3 there's an easier option that doesn't require specifying specific fields or a custom merge function. Instead, you only have to specify the type and it will merge all fields for that type:
const cache = new InMemoryCache({
typePolicies: {
YOUR_TYPE_NAME: {
merge: true,
}
}
});
From your example query, I'd guess that an id field should be available though? Try requesting the ID in your query, that should solve the problem in a much more ideal way.
Had same issue with inconsistency of data values vs. our schema. A value type within an entity was missing the id value. Caused by an incomplete data migration.
Temporary solution:
const typePolicies = {
PROBLEM_TYPE: {
keyFields: false as false,
},
PARENT_TYPE: {
fields: {
PROBLEM_FIELD: {
merge: true
}
}
}
}

graphql passing dynamic data to mutation

haven't used graphql or mongodb previously. What is the proper way to pass objects for the update mutation?
Since the only other way i see to pass multiple dynamically appearing parameters is to use input type which is appears to be a bit ineffective to me (in terms of how it looks in the code, especially with bigger objects), i just pass the possible values themselves. however in this case i need to dynamically construct updateObject, which again, going to get messy for the bigger models.
for example now i did:
Mutation: {
updateHub: async (_, { id, url, ports, enabled }) => {
const query = {'_id': id};
const updateFields = {
...(url? {url: url} : null),
...(ports? {ports: ports} : null),
...(enabled? {enabled: enabled} : null)
};
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
any advise on the better way to handle this?
thanks!
It appears your code could benefit from using ES6 spread syntax -- it would permit you to deal with an arbitrary number of properties from your args object without the need for serial tertiary statements.
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = { ...restArgs };
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
If for some reason you need to explicitly set the undefined properties to null in your object, you could possibly use some a config obj and method like defaults from the lodash library as shown below:
import { defaults } from 'lodash';
const nullFill = { url: null, ports: null, enabled: null }; // include any other properties that may be needed
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = defaults(restArgs, nullFill);
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
Also, FWIW, I would consider placing the dynamic arguments that could be potentially be updated on its own input type, such as HubInput in this case, as suggested in the graphql docs. Below I've shown how this might work with your mutation. Note that because nothing on HubInput is flagged as requird (!) you are able to pass a dynamic collection of properties to update. Also note that if you take this appraoch you will need to properly destructure your args object initially in your mutation, something like { id, input }.
input HubInput {
url: String
ports: // whatever this type is, like [String]
enabled: Boolean
// ...Anything else that might need updating
}
type UpdateHubPayload {
success: Boolean
message: String
hub: Hub // assumes you have defined a type Hub
}
updateHub(id: Int, input: HubInput!): UpdateHubPayload

How to fix "TypeError: Cannot read property 'node' of undefined" when creating pages programmatically in gatsbyjs

I am trying to create a blog using gatsbyjs, and would like for my blog pages to be created programmatically instead of explicitly creating them in the /src/pages folder.
I am currently trying to query the data from contentful, which I managed to do successfully according to GraphiQL. I followed the steps presented in the documentation for the most part, but I keep on encountering this error whenever my program steps into the ".forEach" function.
exports.createPages=({graphql,actions})=>{
const {createPage}=actions
const blogPost= path.resolve('./src/components/blogComponents/blog-post.js')
return new Promise((resolve,reject)=>{
graphql(`
{
allContentfulBlog{
edges{
node{
slug
}
}
}
}
`).then(results=>{
// console.log(results)
if(results.error){
reject(results.error)
}
// create blog post pages
const posts=results.data.allContentfulBlog.edges
console.log(post)
posts.forEach((post,index)=>{
console.log(`showing slugs: ${posts.node.slug}`)
const previous= index === posts.length-1?null: post[index+1].node
const next= index === 0?null: posts[index-1].node
createPage({
path:post.node.slug,
component:blogPost ,
context:{
slug:post.node.slug,
previous,
next
}
})
})
}).then(resolve)
})
This is the schema of the returned result
"data": {
"allContentfulBlog": {
"edges": [
{
"node": {
"slug": "web-developer-roadmap"
}
},
{
"node": {
"slug": "web-fundamentals-1"
}
}
]
}
}
I expected the "forEach" function to loop through all my blogs and assign the appropriate values the "createPage" function, but instead, it keeps on showing telling me that property of node available in my query is not defined even though I confirmed its presence by logging it to the console as can be seen in the "forEach" function.
The problem with your code it that you are trying to access object like an array
const previous= index === post.length-1?null: post[index+1].node
const next= index === 0?null: post[index-1].node
In the above code, post is single object. i:e { node: {} } and you are accessing it like an array post[index+1].node.
const posts =[
{
node: {
slug: "lorem"
}
},
{
node: {
slug: "ipsum"
}
}
];
posts.forEach((post, i) => {
// post is a single object. To access it's node, you need to use post.node
console.log("current post", post);
// To access the next post based on index
if(i<posts.length-1) {
console.log("Next node", posts[i + 1].node);
}
});
If you want to iterate on the next post based on index, use posts[index-1].node. As well make sure to check index, because for last element, index+1 will throw error.

GraphQL query returns error "Cannot return null for non-nullable field"

I have a basic GraphQL query setup as follows:
Query.js:
const Query = {
dogs(parent, args, ctx, info) {
return [{ name: 'Snickers' }, { name: 'Sunny' }];
},
};
module.exports = Query;
schema.graphql:
type Dog {
name: String!
}
type Query {
dogs: [Dog]!
}
I created a function createServer() for starting the server as follows:
const { GraphQLServer } = require('graphql-yoga');
const Mutation = require('./resolvers/Mutation');
const Query = require('./resolvers/Query');
const db = require('./db');
function createServer() {
return new GraphQLServer({
typeDefs: 'src/schema.graphql',
resolvers: {
Mutation,
Query,
},
resolverValidationOptions: {
requireResolversForResolveType: false,
},
context: req => ({ ...req, db }),
});
}
module.exports = createServer;
I then tried querying dogs as follows:
query {
dogs {
name
}
}
But instead of getting the names from the array of dogs, I got the following error instead:
{
"data": null,
"errors": [
{
"message": "Cannot return null for non-nullable field Query.dogs.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"dogs"
]
}
]
}
What seems to be causing this error?
This problem comes from AWS requiring certain standard values in the dynamoDB table, such as createdAt and updatedAd, just add these fields manually with a timestamp in dynamo db for further testing. A mutation always needs to be requested via id, this somehow was not clear to me when my schema was created by amplify codegen...
The above code works as you can see in codesandbox: https://codesandbox.io/s/olzj9vvpk5
But when I convert Query to something like {} it returns the same error so please check your paths and console.log Query to validate the path. Your export looks correct but you might have forgotten to save the file as I can see from the course starter files Query is an {}. Please double check.
Also if this code is in a public git repo please share the link.
I know this question has been answered, but for me the only thing that fixed this issue was to also pass the info argument.
In my case, I create a new Query.js file at the src folder but I import Query with Query = require('./resolvers/Query') and coding there. So, try to check the path, I think the problem is there.

Categories

Resources