I have this single type in my strapi dashboard :
I Have a component called Logo
Another component called Links, it contains another component called Link
Finally a component called MenuButton.
When I go to http://localhost:1337/api/global?populate=* I got :
{
"data": {
"id": 1,
"attributes": {
"createdAt": "2021-12-27T11:54:36.177Z",
"updatedAt": "2021-12-27T11:54:54.737Z",
"publishedAt": "2021-12-27T11:54:54.731Z",
"logo": {
"id": 1,
"name": null
},
"navigation": {
"id": 1 // why I don't get links here ?
},
"menuButton": {
"id": 1,
"icon": ""
}
}
},
"meta": {
}
}
I Already published my content and allowed permissions for public.
My question is :
How can I access to the links inside navigation object ?
See my earlier answer here
Strapi 4 requires you to populate your request (see: population documentation )
which could look like this (for level 2 population):
// populate request
const qs = require('qs')
const query = qs.stringify(
{
populate: {
Product: {
populate: ['Image']
}
}
},
{
encodeValuesOnly: true
}
)
// get id
const id = yourId
// get rquest
const Response= await axios.get(
`http://localhost:1337/api/[your api]/${id }/?${query}`
)
Now media links should be included in your response
To retrieve up to 5 levels deep, you can install this package npm i strapi-plugin-populate-deep
I'm testing a component that is using a graphql useLazyQuery. MockProvider is provided by the Apollo recommended library #apollo/react-testing. I want to test that a certain message is being rendered base off the length of the data that is returned from the query. I have html elements structured like this:
<div className="message" data-id={props.data ? props.data.specials.length > 0 ? 'valid' : 'invalid' : ''}>
...children
</div>
I read through Apollo's docs about testing and wrote up a test like this:
mock = {
request: {
query: GET_PRODUCTS,
variables: { zip: "91001" }
},
result: {
data: {
specials: [
{
"_id": "5ecf28c459d3781a2e99738e",
},
{
"_id": "5ecf28c459d3781a2e99738f",
}
]
}
}
}
wrapper = mount(
<MockedProvider mocks={[mock]} addTypename={false}>
<Store.Provider value={[{ loading: false, zip: null }]}>
<GetZipCode />
</Store.Provider>
</MockedProvider>
)
await wait(0)
expect(wrapper.find(message).prop('data-id')).toEqual('valid')
But I've found that the tests do not update based on the mock that I put it. I have a test about this where I'm passing this value as the mock:
mock = {
request: {
query: GET_PRODUCTS,
variables: { zip: "32005" }
},
result: {
data: { specials: [] }
}
}
...after tests
expect(wrapper.find(message).prop('data-id')).toEqual('invalid')
And for both of these tests the expected value is "" which is the initial value for my data-id prop in my html element. If I were to set the initial value to "invalid" compared to "" then the expected value in my test would output "invalid".
It seems that no matter what I passed my mock provider it doesn't wait for it to be passed. I'm using the wait package that Apollo recommends as well.
If you wanna mock using jest, you use the following approach
jest.mock("apollo-client", () => ({
__esModule: true,
useQuery: (query: any) => {
//other mocks if needed
},
useLazyQuery: jest.fn().mockReturnValue([
jest.fn(),
{
data: {
yourProperties: "Add Here",
},
loading: false,
},
]),
}));
As you see, this approach even returns the mock function that is called in the code to be tested.
I faced in similar issues while testing with useLazyQuery. I would suggest writing a custom hook on top of useLazyQuery. This will have two benefits:
No need to wrap your test instance in Mock provider. You can mock the entire module (custom hook) using jest and use mockReturnValue method to simulate different behaviours (loading, data, error)
You can easily switch between actual query or some mock data in local file system while development.
Refer this blog for implementation details and demo app.
I am using the Strapi v3.0.0-beta.18.7 UI and the error in the UI are shown partly so it is impossible to read the full text of the error message.
I suggest use a custom middleware to manage your needs.
Here is the documentation to create a middleware - https://strapi.io/documentation/3.0.0-beta.x/concepts/middlewares.html
Step 1: Create the middleware
Path — middlewares/log/index.js
module.exports = strapi => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
await next();
const status = ctx.status;
if (status < 200 || status >= 300) {
console.log(ctx.body);
}
});
},
};
};
Step 2: Enable the middleware
Path — config/environments/development/middleware.json
{
"log": {
"enabled": true
}
}
Step 3: Set the middleware in the right order
Path — config/middleware.json
{
"timeout": 100,
"load": {
"before": [
"log",
"responseTime",
"logger",
"cors",
"responses",
"gzip"
],
"order": [
"Define the middlewares' load order by putting their name in this array is the right order"
],
"after": [
"parser",
"router"
]
}
}
The launch (New Session), unhandled, and Amazon default intents (cancel, help, stop) are working properly when I test them in the service simulator, but any one that I write doesn't seem to work. Below is an example of a test intent:
var handlers = {
'NewSession': function() {
this.emit(':tell', 'Hello');
'Test': function() {
this.emit(':tell','This intent is working');
},
'Unhandled': function() {
this.emit(':tell','Sorry, I don\'t know what to do');
},
'AMAZON.HelpIntent': function(){
this.emit(':ask', 'What can I help you with?', 'How can I help?');
},
'AMAZON.CancelIntent': function(){
this.emit(':tell', 'Okay');
},
'AMAZON.StopIntent': function(){
this.emit(':tell', 'Goodbye');
},
exports.handler = function(event,context){
var alexa = Alexa.handler(event,context);
alexa.registerHandlers(handlers);
alexa.execute();
};
The code snippet for the intents:
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "ColorIntent",
"samples": [],
"slots": [
{
"name": "Test",
"samples": [
"Test me"
],
"slots": []
No matter what I do, I can't get the test intent to work and return "This intent is working'. Please help!
Maybe it's because your first invocation always gets handled by the NewSession handler, which then responds with 'Hello' and ends the session.
I see two ways to activate your Test handler, i.e. to get your Skill to respond with 'This intent is working':
You create a multi-turn conversation by ending your NewSession handler with this.emit(':ask', 'Hello! What do you want to do next?');, and then uttering 'Test me'.
You replace your NewSession handler with a LaunchRequest handler, and invoke your Skill with 'Alexa, tell tie picker to test me'.
Hope that helps! :)
By the way, because I can't comment everywhere yet: You can log your lambda's state, e.g. for debugging, by using console.log( 'Test handler invoked');, and then looking up the logs in AWS CloudWatch.
Pretty straightforward (I hope). I'd like to be able to use the API endpoint and have it only return specified fields. I.E. something like this
http://localhost:1337/api/reference?select=["name"]
Would ideally return something of the form
[{"name": "Ref1"}]
Unfortunately that is not the case, and in actuality it returns the following.
[
{
"contributors": [
{
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
}
],
"createdBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"updatedBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"question": {
"createdBy": 1,
"createdAt": "2016-07-28T19:41:33.152Z",
"template": "default",
"lang": "en_US",
"name": "My Question",
"content": "Cool stuff, huh?",
"updatedBy": 1,
"updatedAt": "2016-07-28T19:45:02.893Z",
"id": "579a5ff83af4445c179bd8a9"
},
"createdAt": "2016-07-28T19:44:31.516Z",
"template": "default",
"lang": "en_US",
"name": "Ref1",
"link": "Google",
"priority": 1,
"updatedAt": "2016-07-28T19:45:02.952Z",
"id": "579a60ab5c8592c01f946cb5"
}
]
This immediately becomes problematic in any real world context if I decide to load 10, 20, 30, or more records at once, I and end up loading 50 times the data I needed. More bandwidth is used up, slower load times, etc.
How I solved this:
Create custom controller action (for example, 'findPaths')
in contributor/controllers/contributor.js
module.exports = {
findPaths: async ctx => {
const result = await strapi
.query('contributor')
.model.fetchAll({ columns: ['slug'] }) // here we wait for one column only
ctx.send(result);
}
}
Add custom route (for example 'paths')
in contributor/config/routes.json
{
"method": "GET",
"path": "/contributors/paths",
"handler": "contributor.findPaths",
"config": {
"policies": []
}
},
Add permission in admin panel for Contributor entity, path action
That's it. Now it shows only slug field from all contributor's records.
http://your-host:1337/contributors/paths
Here is how you can return specific fields and also exclude the relations to optimize the response.
async list (ctx) {
const result = await strapi.query('article').model.query(qb => {
qb.select('id', 'title', 'link', 'content');
}).fetchAll({
withRelated: []
}).catch(e => {
console.error(e)
});
if(result) {
ctx.send(result);
} else {
ctx.send({"statusCode": 404, "error": "Not Found", "message": "Not Found"});
}
}
I know this is old thread but I just run into exactly same problem and I could not find any solution. Nothing in the docs or anywhere else.
After a few minutes of console logging and playing with service I was able to filter my fields using following piece of code:
const q = Post
.find()
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
return filterFields(q, ['title', 'content']);
where filterFields is following function:
function filterFields(q, fields) {
q._fields = fields;
return q;
}
It is kinda dirty solution and I haven't figured out how to apply this to included relation entites yet but I hope it could help somebody looking for solution of this problem.
I'm not sure why strapi does not support this since it is clearly capable of filtering the fields when they are explicitly set. it would be nice to use it like this:
return Post
.find()
.fields(['title', 'content'])
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
It would be better to have the query select the fields rather than relying on node to remove content. However, I have found this to be useful in some situations and thought I would share. The strapi sanitizeEntity function can include extra options, one of which allows you only include fields you need. Similar to what manually deleting the fields but a more reusable function to do so.
const { sanitizeEntity } = require('strapi-utils');
let entities = await strapi.query('posts').find({ parent: parent.id })
return entities.map(entity => {
return sanitizeEntity(entity, {
model: strapi.models['posts'],
includeFields: ['id', 'name', 'title', 'type', 'parent', 'userType']
});
});
This feature is not implemented in Strapi yet. To compensate, the best option for you is probably to use GraphQL (http://strapi.io/documentation/graphql).
Feel free to create an issue or to submit a pull request: https://github.com/wistityhq/strapi
You can use the select function if you are using MongoDB Database:
await strapi.query('game-category').model.find().select(["Code"])
As you can see, I have a model called game-category and I just need the "Code" field so I used the Select function.
In the current strapi version (3.x, not sure about previous ones) this can be achieved using the select method in custom queries, regardless of which ORM is being used.
SQL example:
const restaurant = await strapi
.query('restaurant')
.model.query((qb) => {
qb.where('id', 1);
qb.select('name');
})
.fetch();
not very beautiful,but you can delete it before return.
ref here:
https://strapi.io/documentation/developer-docs/latest/guides/custom-data-response.html#apply-our-changes
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async find(ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.services.restaurant.search(ctx.query);
} else {
entities = await strapi.services.restaurant.find(ctx.query);
}
return entities.map(entity => {
const restaurant = sanitizeEntity(entity, {
model: strapi.models.restaurant,
});
if (restaurant.chef && restaurant.chef.email) {
**delete restaurant.chef.email;**
}
return restaurant;
});
},
};
yeah,I remember another way.
you can use the attribute in xx.settings.json file.
ref:
model-options
{
"options": {
"timestamps": true,
"privateAttributes": ["id", "created_at"], <-this is fields you dont want to return
"populateCreatorFields": true <- this is the system fields,set false to not return
}
}
You can override the default strapi entity response of:-
entity = await strapi.services.weeklyplans.create(add_plan);
return sanitizeEntity(entity, { model: strapi.models.weeklyplans });
By using:-
ctx.response.body = {
status: "your API status",
message: "Your own message"
}
Using ctx object, we can choose the fields we wanted to display as object.
And no need to return anything. Place the ctx.response.body where the response has to be sent when the condition fulfilled.
It is now 2023, and for a little while it has been possible to do this using the fields parameter:
http://localhost:1337/api/reference?fields[0]=name&fields[1]=something