In the example
on the site
Strapi Docs
To populate specific relations, one or several levels deep, use the LHS bracket notation for fields names in combination with the populate parameter. The qs library (opens new window)is helpful to build complex URLs:
const qs = require('qs');
const query = qs.stringify({
populate: {
author: {
populate: ['company'],
}
}
}, {
encodeValuesOnly: true,
});
await request(`/api/articles?${query}`);
// GET /api/articles?populate[author][populate][0]=company
How to fill relationships, two three or more several levels deep?
In my example, I tried
/api/regions?populate[cities][populate][0]=holding?spopulate[companies][populate][0]=holdings
The answer to this question was posted here. Make sure you're using Strapi v4 and simply install the plugin to the Strapi server.
Related
Hi I’m migration from strapi v3 to v4. I’m stuck for quite sometime regarding population of relations in v4.
BEHAVIOUR IN V3
Previously in v3 if I queried a service, it populated uptill the 2nd level by default if I’m not wrong, and in the second level if returned foreign keys from the further nested tables/relations respectively.
Example case I have following relations:
activity → cities → province , [images]
Querying activity like this in code:
const activity = await strapi.services.activity.findOne({ id });
would return activity → cities → { provinceforeignkey , images }
e.g. sample response
{
id: activity_id,
cities: [
id,
province: id_of_province,
images: [
// IMAGES DETAILED RESPONSE
]
]
}
BEHAVIOUR IN V4
I’m not able to get the desired response as above either by direct querying:
const activity = await strapi
.service("api::activity.activity")
.findOne(id,populate: ["cities"]);
Or by using entityService:
await strapi.entityService.findOne(
"api::activity.activity",
id,
populate: ["cities"]
);
I know we can use populate field to populate the desired relation, but on second level e.g. province case inside cities I need the id of province not the entire object population.
This is really important to us as we’ve tonnes of custom apis and custom code already implemented in v3 as per the following approach, otherwise we would have to go and change each case specifically in frontend and backend.
Can anyone guide me on this?
the entityService and db.query accept fields parameter that is undocumented but may work? However, what i would recommend is doing your own normalize function like:
let activities = await strapi.db.query('api::activity.activity')
.findOne({where: { id }, populate: { cities: { populate: ['state'] } } );
return normalizeManyActivities(activities)
and
const normalizeActivity = (activity) =>
({...activity,
cities: activity.cities.map(city =>
({...city, state: city.state.id })
})
);
const normalizeManyActivities = (activities) =>
activities.map(activity => normalizeActivity(activity));
and the second approach is to use middleware's witch you can take reference from here:
https://www.youtube.com/watch?v=YIbhKm1o0fE
I'm building a website with a blog section, and on deployment to production the blog will be empty. I'm having problems allowing an empty blog on my Gatsby site.
When I run npm run develop it will only work if I have some blogs - I want it to work before the blogs have been added.
The main issues I'm encountering is trying to accomidate fields not existing like allStrapiBlog and strapiBlog because there are no blogs.
I get errors like this on blog components, and on my nav component (where i have a query that a conditional uses).
15:17 error Cannot query field "allStrapiBlog" on type "Query" graphql/template-strings
Cannot query field "strapiBlog" on type "Query"
This is what the query looks like for my navigation component. But it throws an error - is there a way to make it just return null?
query NavigationQuery {
allStrapiBlog {
nodes {
title
strapi_id
}
totalCount
}
}
How do I make unsuccessful GraphQL queries not break the build, so I can build a gatsby site with a empty blog?
But it throws an error - is there a way to make it just return null?
Indeed, you need to configure your GraphQL schema to allow nullable fields.
You have a boilerplate that you can tweak to match your data types at https://www.virtualbadge.io/blog-articles/nullable-relational-fields-strapi-gatsbyjs-graphql.
The idea relies on using the createSchemaCustomization API in your gatsbt-node.js to add your own type definitions.
Something like:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type StrapiBlogPost implements Node {
title: String!
content: String
thumbnail: File
}
`;
createTypes(typeDefs);
};
In this case, the title is required (because of the !, which means that the type is non-nullable) while content and thumbnail can be null.
Afterward, you will only need to adapt your component to avoid code-breaking logics when null data is fetched.
Lets say i create an entry where i can select two collections like this
now , how to detec when someone changed first collection and then apply some filter to data in second collection ?
not sure if you're still looking for the answer but your question ranked high on my search so here's an answer for future visitors.
Say you add a post entry for your Posts collection. In the ./api/post/models/post.js you can create a hook like this
'use strict';
module.exports = {
lifecycles: {
async afterCreate(result, data) {
const id = result.streakID;
const streak = strapi.services.streaks.findOne({ id });
strapi.services.streaks.update({ id }, { counter: streak.counter++ });
},
},
};
My source
Best regards
EDIT: the hook runs on Post creation and accesses the Streak model by way of strapi.services.streaks. To be clear.
I'm using Graph.cool graphql as a service and am wondering how to do a mass update to the collection, similar to a SQL update.
In my case I need to update the suffix of a url, in the imageUrl column of my database. I need to swap out a {someid}_sm.jpg to {someid}_lg.jpg
How do I do that with a graphql mutation? I don't want to reload the entire dataset again and am looking for a way to do it that doesn't involve manually interating through the entire list with a graphql client.
mutation {
updatePost() // what goes here?
}
Migration script
The best approach is indeed to use a migration script that combines multiple mutations so only one HTTP request is sent to the GraphQL backend.
Consider this schema:
type Image {
id: ID!
name: String!
}
We can include the same mutation multiple times in one request with GraphQL aliases:
mutation {
first: updateImage(id: "first-id", name: "01_lg.jpg") {
id
name
}
second: updateImage(id: "second-id", name: "02_lg.jpg") {
id
name
}
}
We'll make use of this mechanism in our migration script. I'll describe it with Lokka and Node, however you can choose whatever language and GraphQL client you prefer.
First, we query all existing images to obtain their id and name:
const queryImages = async() => {
const result = await client.query(`{
images: allImages {
id
name
}
}`)
return result.images
}
Then we replace the names accordingly and construct one big request including the necessary updateImage mutations with a different GraphQL alias for each.
If your image names might contain the string sm in the {someid} part mentioned in your question, this script will break! In that case, please adjust accordingly.
const migrateImages = async(images) => {
// beware! if your ids contain the string 'sm', adjust the string replacement accordingly!
const updateMutations = _.chain(images)
.map(image => ({ id: image.id, name: image.name.replace('sm', 'lg')}))
.map(image => `
${image.id}: updateImage(id: "${image.id}", name: "${image.name}") {
id
name
}`)
.value()
.join('\n')
const result = await client.mutate(`{
${updateMutations}
}`)
console.log(`Updated ${Object.keys(result).length} images`)
console.log(result)
}
That's it. If you have to update thousands of images, batching the mutations in say groups of a hundred might be better than to batch all of them in one request. Note that mutations run sequentially on the GraphQL server.
Running the migration
Currently, I suggest the following workflow for running the migration:
Clone your project
Run the migration script on your cloned project
Verify that the migration ran successfully. Double check :)
Run the migration on your original project
You can find the code and further instructions here.
While this approach is great for migrations that are as straightforward as in your example, it's not perfect for all situations. We're already thinking about creating an integrated experience for this use case, such as an interactive migration right in your Graphcool project, with simulated migrations, checks and more. If you have suggestions, let me know in Slack.
I'm very new to developing mobile applications with telerik appbuilder. There are some things I have a hard time to understand with fetching data from Everlive.
Lets consider a simple example. Lets say I have Blog Posts and Comments that belong to those Posts. And both Posts and Comments are made by Users.
In one view I want to present the Post with corresponding Comments and I also need the Username of the User who posted the Comment (Comment table only contains userId).
Both the Post and the Comments are easy to fetch since I have the id of the Post. But how do I fetch the corresponding user for each Comment?
The FriendsApp example does something very similar but it uses this line to get the user:
var user = $.grep(app.Users.users(), function (e) {
return e.Id === userId;
})[0];
This fetches all users and filters them client side? I guess this is okay if you have like 10 users. But what if you have a million users?
I am guessing that the FriendsApp uses this way of resolving the relations just for keeping the simplicity of the sample. Everlive offers a far more meaningful toolset for resolving relation fields called Expand. You can explore the REST API here:
http://docs.telerik.com/platform/backend-services/development/rest-api/relations/simple-expanding
or the JS SDK function here:
http://docs.telerik.com/platform/backend-services/development/javascript-sdk/relations/simple-expanding.
As the Friends app uses the Kendo UI data source component you can send an Expand header with the request. The following configuration of the data source will return the DisplayName of the user in each Activity/Comments entity:
var expandObject = {
"CreatedBy": {
"ReturnAs": "User",
"SingleField": "DisplayName"
}
};
var dataSource = new kendo.data.DataSource({
type: "everlive",
transport: {
typeName: 'Activities', // replace this with Comments
read: {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-Everlive-Expand", JSON.stringify(expandObject))
},
}
},
schema: {
model: {
id: Everlive.idField
}
}
});
dataSource.fetch(function (data) {
console.log(data.items);
});
Same could be applied for resolving the comments for each Blog post. Given the Friends data schema you will need to use the External Relation resolver of the Everlive API. Note that it is available only in a GetById scenario, e.g. when retrieving an Activity by Id, you can resolve the Comments that point to this activity, which is generally very handy in master-detail views.