I will make it simple:
Link: http://localhost:3000/product/bioCloths?activeCategory=medium&id=0
File path: pages/product/[product].js.
Expected: product: "bioCloths, activeCategory: "medium", id: "0"
using getStaticProps at [product].js:
const ProductDetails = (props) => {
console.log("initial props", props);
return <div>product details...</div>
};
export default ProductDetails;
export async function getStaticProps(context) {
return {
props: {
context: context
},
};
}
export async function getStaticPaths() {
return {
paths: [{ params: { product: "1" } }, { params: { product: "2" } }],
fallback: true,
};
}
Props returns: context: params: product: "bioCloths", excluding the query params.
Now if I use the deprecated getInitialProps instead:
ProductDetails.getInitialProps = (context) => {
const activeCategory = context.query.activeCategory;
const id = context.query.id;
const product = context.query.product;
return {
activeCategory: activeCategory,
id: id,
product: product,
};
};
props logs activeCategory: "medium" id: "0" product: "bioCloths"
I need to get these all of these props so I can fetch data before the client mounts.
I understand that getInitialProps is now deprecated, but it works. Why is not getStaticProps working, and should I use it instead of serverSideProps?
This is an ecommerce, and there is no way I can set getStaticPaths for hundreds of possibilities, to work along with getStaticProps so I guess that in this case I should use getInitialProps or getServerSideProps?
P.S - getServerSideProps hits me an error stating that it will only accept .json files.
According to one of the maintainers of Nextjs this is the reply for anyone learning this framework:
getStaticProps generates the page at build time. There's no possible way to know the custom query params your visitors can use at build time.
getInitialProps and getServerSideProps can know all the possible query params because the page is generated at runtime, whenever you receive a new request.
You can learn more about the above here.
"
This discussion can be read on Github
If your page uses a dynamic route, params contain the route parameters. For instance, If the page name is [id].js, then params will look as follows:
{ id: /* something */ }
You can get URL params from inside your getStaticProps or getServerSideProps function with the context argument.
Here’s an example with getStaticProps:
// pages/[id].js
export async function getStaticProps(context) {
const { params } = context;
const id = params.id;
const data = /* Fetching data with the id */
return {
props: data,
}
}
You can do the same with getServerSideProps:
// pages/[id].js
export async function getServerSideProps(context) {
const { params } = context;
const id = params.id;
/* ... */
}
I have the following model defined:
import db from "../connection.js";
import objection from "objection";
const { Model } = objection;
Model.knex(db);
class User extends Model {
static get tableName() {
return "users";
}
static get relationMappings() {
return {
emails: {
relation: Model.HasManyRelation,
modelClass: Email,
join: {
from: "users.id",
to: "emails.user_id",
}
}
}
}
}
class Email extends Model {
static get tableName() {
return "emails";
}
static get relationMappings() {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: User,
join: {
from: "emails.user_id",
to: "users.id",
},
},
};
}
}
And to query a User with their email addresses would require withGraphFetched() to be explicitly run every time as:
const myUser = await User.query().withGraphFetched("emails").findById(1)
I haven't been able to figure out what to write in the Model definition to make this possible, and I don't see any such examples online. Is it possible to ALWAYS have withGraphFetched("emails") automatically included in the query so it doesn't have to be explicitly written out every time?
Such thing doesnt exist. Maybe you could create the logic in the beforeFind hook adding the eager loader to the knex instance, but it could generate a lot of undesired and strange side-effects.
The normal practice is adding a method to that specific case:
class User extends Model {
static get tableName() {
return "users";
}
static get relationMappings() {
return {
emails: {
relation: Model.HasManyRelation,
modelClass: Email,
join: {
from: "users.id",
to: "emails.user_id",
}
}
}
}
static async getUser (id) { // Or maybe getUserWithEmails
return await this.query().withGraphFetched("emails").findById(id)
}
}
Then you can just:
const myUser = await User.getUser(id)
Getting this error from Apollo:
core.js:14576 ERROR Error: Network error: Error writing result to store for query:
{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AdditionalServices"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"vendorID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},"directives":[]}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"vendor"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"vendorID"}}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"services"},"name":{"kind":"Name","value":"products"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"productTypes"},"value":{"kind":"EnumValue","value":"service"}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"isActive"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"cartSection"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"imageUrl"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"shortDescription"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}}]}}],"loc":{"start":0,"end":372}}
Store error: the application attempted to write an object with no provided id but the store already contains an id of Restaurant:200 for this object. The selectionSet that was trying to be written is:
{"kind":"Field","name":{"kind":"Name","value":"vendor"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"vendorID"}}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"services"},"name":{"kind":"Name","value":"products"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"productTypes"},"value":{"kind":"EnumValue","value":"service"}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"isActive"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"cartSection"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"imageUrl"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"shortDescription"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"name"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}}
at new ApolloError (ApolloError.js:25)
at QueryManager.js:276
at QueryManager.js:638
at Array.forEach (<anonymous>)
at QueryManager.js:637
at Map.forEach (<anonymous>)
at QueryManager.push../node_modules/apollo-client/core/QueryManager.js.QueryManager.broadcastQueries (QueryManager.js:632)
at QueryManager.js:226
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
at Object.onInvoke (core.js:16135)
This is the code that makes this happen:
this.restaurantID$.pipe(
takeUntil(this._ngOnDestroy)
)
.subscribe((restaurantID) => {
this.additionalServicesQuery$.next(this._apollo
.watchQuery<AdditionalServices>({
query: AdditionalServicesQuery,
variables: { vendorID: restaurantID }
}));
});
const loadAdditionalServicesData = this.additionalServicesQuery$
.pipe(
takeUntil(this._ngOnDestroy),
filter((query) => !!query),
switchMap((query) => query.valueChanges), // This is the switchMap that makes it happen
takeUntil(this._ngOnDestroy),
map((response) => response.data.vendor.services.nodes)
);
There is a SwitchMap that I commented if that is removed, the error does not happen. I can't understand what is going on.
query:
export const AdditionalServicesQuery = gql`
query AdditionalServices(
$vendorID: ID!
) {
vendor(
id: $vendorID
) {
services: products (productTypes: service) {
nodes {
id
isActive
cartSection {
id
name
}
description
imageUrl
shortDescription
name
}
}
}
}
`;
Update:
Added ID to query, still, same issue
export const AdditionalServicesQuery = gql`
query AdditionalServices(
$vendorID: ID!
) {
vendor(
id: $vendorID
) {
services: products (productTypes: service) {
id
nodes {
id
isActive
cartSection {
id
name
}
description
imageUrl
shortDescription
name
}
}
}
}
`;
According to that error, you need to add an id field (or _id field, whichever exists) to the selection set for the vendor field. Sounds like you already have another query that returns objects of the type Restaurant, that query included the id and was normalized properly. Apollo won't be able to combine the individual Restaurant objects from both queries unless an id is included.
export const AdditionalServicesQuery = gql`
query AdditionalServices(
$vendorID: ID!
) {
vendor(
id: $vendorID
) {
id # <----------------------------------------- ADD ME
services: products (productTypes: service) {
nodes {
id
isActive
cartSection {
id
name
}
description
imageUrl
shortDescription
name
}
}
}
}
`;