How to query collections from custom plugin in strapi? - plugins

I want to collect data from my collections and display it in my own plugin, for example 'Cars'. I have not found anything about this and do not know how to approach this.
import React, { memo } from 'react';
import pluginId from '../../pluginId';
const HomePage = () => {
const fetchData = () => {
// Here I want to fetch data from my collection and display it
return null;
}
return (
<div>
<h1>{pluginId}&apos;s HomePage</h1>
<p>Happy coding</p>
{fetchData()}
</div>
);
};
export default memo(HomePage);

Old question but I've been looking for the answer and it's difficult to find.
So the solution for this, is to use the endpoints provided by the content-manager plugin of strapi.
First you should go and allow public access to this endpoints in Settings then Roles & Permissions plugin.
Finally you can query your data like this
const response = await request("/content-manager/collection-types/application::cars.cars", {
method: "GET"
});
}

Case : Api model :
const cars = await strapi.query('car').find({});
Case : Plugin model :
const cars = await strapi.query('car', 'plugin_name').find({});

Related

Decorator for getting current user NestJs + MongoDB

I'm using NestJs + MongoDb
This custom decorator doesn't give me current user. I get the first user from my database.
I've checked this from different users and I always get the same answer.
Decorator:
export const CurrentUser = createParamDecorator((data, req): UserModel => {
const request = req.switchToHttp().getRequest();
return request.user
});
Controller
#UseGuards(AuthGuard('jwt'))
#UsePipes(new ValidationPipe())
#HttpCode(200)
#Patch(':friendId')
#Auth()
async toggleFriend(
#CurrentUser() user: UserModel,
) {
console.log('CurrentUser', user)
}
Study below section, from it you will be able to understand how information is added to the authorization token and then further extraction of this information.
https://docs.nestjs.com/security/authentication#jwt-functionality

Populate a database with datas from a graphql api

I'm searching, as the topic name says, to populate a database with datas coming from an already exist graphql API.
My project is using Next.js, PostgreSQL, Apollo for GraphQL and Prisma to connect the whole. I managed to retrieve datas from the API and display these. Now I just want to store these datas in my Postgres database. So you know I'm using the Client-Side Rendering and not the other methods (SSR, SSG).
All documentations I found was about creating an API then post to the database which is not my case or documentation about create & createMany queries but with hardcoded datas. My research doesn't help me to see clear so I come here to find anyone who is willing to guide me. If have a clue I'll appreciate. Thank you =) !
To illustrate here a piece of code :
import { useQuery, gql } from "#apollo/client";
// query to retrieve 3 animes
const QUERY = gql`
query GetFirstsThree{
Page(page: 1, perPage: 3) {
media {
title {
userPreferred
}
}
}
}
`;
export default function AnimList() {
const { data, loading, error } = useQuery(QUERY);
if (loading) {
return <h2>Loading...</h2>;
}
if (error) {
return null;
}
const medias = data.Page.media;
return (
<div className="mainGrid">
{medias.map((value) => {
return (
<p>{value.title.userPreferred} </p>
)
})}
</div>
);
}
Hello for those who have the same issue here is how i resolved my problem.
First I have created a async function to post my Anime.
async function savedAnime(anime){
const res = await fetch('api/anime', {
method: 'Post',
body: JSON.stringify(anime),
});
if(!res.ok){
throw new Error('Something went wrong');
}
return await res.json();
}
Then I added a submit input, which on its onClick event will map the medias the same way I did to display the anime's title, but here to store the datas in the database.
After, I create a variable with my datas that I want to store. Finally, I call the function I've created before to post my Anime, on which I pass the variable.
And that pretty much all.
<input type="submit" value="Save Animes" onClick={ async () => {
try{
medias.map(async (value) => {
const anime = {
title: value.title.userPreferred,
coverImage: value.coverImage.medium,
};
console.log(anime);
savedAnime(anime);
})
} catch(err){
console.error(err);
}
}}/>

Algolia: How to pass refinements from a front-end to a backend?

I have a webpage that uses Algolia's React InstantSearch. It has a search bar and several refinements.
I want the user to be able to press a button and get a list of all matching results.
To get a list of all results, I need to use the Browse Index instead of the Search Index. The Browse Index allows retrieving all hits; the Search Index allows retrieval of only up to 1000 hits. However, the Browse Index should not be used in UIs. So I want to create an API endpoint my web server that uses the Browse Index in order return a list of matching hits given a search query.
I am able to successfully do this for a search query, but I can't figure out how to this for refinements.
Here is a sketch of what I have so far.
Back-end (in Ruby):
ALGOLIA_INDEX = Algolia::Index.new('Products')
class AlgoliaSearchController < ActionController::Base
def get_search_results
query = params['query']
hits = []
ALGOLIA_INDEX.browse({query: query}) do |hit|
hits << hit
end
render json: hits
end
end
Frontend:
import qs from 'qs';
import React, { useCallback, useState } from 'react';
import { InstantSearch } from 'react-instantsearch-dom';
function getSearchResults(query) {
const queryString = qs.stringify({
query,
})
return fetch(`/search_results?{queryString}`);
}
function App() {
const [searchState, setSearchState] = useState(null);
const onSearchStateChange = useCallback(searchState => {
setSearchState(searchState);
}, [searchState]);
const onClick = useCallback(() => {
console.log(getSearchResults(searchstate.query));
});
return (
<InstantSearch ... onSearchStateChange={onSearchStateChange}>
<button onClick={onClick}>Search</button>
</InstantSearch>
);
}
I can't find any resources that explain how to do search with refinements.
Things I've looked at so far:
I can try to map the searchState format to the Search API Parameters used by the Browse Index. I could write my own mapper from search state to a query, however, 1) this seems complex and I suspect I'm missing something simpler and 2) this seems like this should be open-sourced somewhere since I suspect I'm not the first to run into this issue.
There is an article, Backend InstantSearch
, that explains how to write a backend that can be plugged into the InstatSearch component. However it doesn't explain how I could do a one-off search from the search state.
You are right that this is currently not exactly straightforward. The flow to get the raw search parameters you can use for "browse" is like this:
give your custom component access to the last search results
read the state from those results
create a new helper using this state
use helper.getQuery() to get the query parameters to apply
A sandbox that illustrates this is: https://codesandbox.io/s/extending-widgets-luqd9
import React, { Component } from 'react';
import algoliaHelper from 'algoliasearch-helper';
import { connectStateResults } from 'react-instantsearch-dom';
class Downloader extends Component {
state = {
instructions: '',
};
onDownloadClick = () => {
// get the current results from "connectStateResults"
const res = this.props.searchResults;
// read the private "state" (SearchParameters) from the last results
const state = res && res._state;
// create a new "helper" with this state
const helper = algoliaHelper({}, state.index, state);
// get the query parameters to apply
const rawQuery = helper.getQuery();
this.setState({
instructions:
'Do a search backend with this:\n\nclient.browseAll(' +
JSON.stringify(rawQuery, null, 2) +
')',
});
};
render() {
return (
<React.Fragment>
<button onClick={this.onDownloadClick}>download</button>
<pre>{this.state.instructions}</pre>
</React.Fragment>
);
}
}
export default connectStateResults(Downloader)

What's the equivalent of Angular Service in VueJS?

I want to put all my functions that talk to the server and fetch data into a single reusable file in VueJS.
Plugins don't seem to be the best alternative. Template less components..?
In total there are 4 ways:
Stateless service: then you should use mixins
Stateful service: use Vuex
Export service and import from a vue code
any javascript global object
I am using axios as HTTP client for making api calls, I have created a gateways folder in my src folder and I have put files for each backend, creating axios instances, like following
myApi.js
import axios from 'axios'
export default axios.create({
baseURL: 'http://localhost:3000/api/v1',
timeout: 5000,
headers: {
'X-Auth-Token': 'f2b6637ddf355a476918940289c0be016a4fe99e3b69c83d',
'Content-Type': 'application/json'
}
})
Now in your component, You can have a function which will fetch data from the api like following:
methods: {
getProducts () {
myApi.get('products?id=' + prodId).then(response => this.product = response.data)
}
}
As I assume you want to re-use this method in multiple components, you can use mixins of vue.js:
Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.
So you can add a method in mixin and it will be available in all the components, where mixin will be mixed. See following example:
// define a mixin object
var myMixin = {
methods: {
getProducts () {
myApi.get('products?id=' + prodId).then(response => this.product = response.data)
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin]
})
// alternate way to have a mixin while initialising
new Vue({
mixins: [myMixin],
created: function () {
console.log('other code')
}
})
I'm using Vue Resource mostly.
1.I create new file where I do connection to API endpoint using Vue.http.xxx.So let's say we have endpoint that output the posts.Create new directory in your project, I call it services, and then create file called PostsService.js - content looks like this:
import Vue from 'vue'
export default {
get() {
return Vue.http.get('/api/posts)
}
}
Then I go to component where I want use this service, and import it
import PostsService from '../services/PostsService'
export default {
data() {
return {
items: []
}
},
created() {
this.fetchPosts()
},
methods: {
fetchPosts() {
return PostsService.get()
.then(response => {
this.items = response.data
})
}
}
}
For more info about this approach, feel free to check my repo on GitHub https://github.com/bedakb/vuewp/tree/master/public/app/themes/vuewp/app
I suggest creating an API Provider that you can access from anywhere in your app.
Simply create a src/utils folder and inside of it a file called api.js.
In it, export your wrapper that knows how to communicate with your API as an object or a ES6 static class (I prefer how the latter looks and works if you're not afraid of classes). This provider can use any HTTP request library that you like and you can easily swap it later by changing a single file (this one) instead of hunting down the whole codebase. Here's an example of using axios, assuming we have a REST API available at api.example.com/v1 that uses SSL:
import axios from 'axios'
import { isProduction, env } from '#/utils/env'
const http = null // not possible to create a private property in JavaScript, so we move it outside of the class, so that it's only accessible within this module
class APIProvider {
constructor ({ url }) {
http = axios.create({
baseURL: url,
headers: { 'Content-Type': 'application/json' }
})
}
login (token) {
http.defaults.headers.common.Authorization = `Bearer ${token}`
}
logout () {
http.defaults.headers.common.Authorization = ''
}
// REST Methods
find ({ resource, query }) {
return http.get(resource, {
params: query
})
}
get ({ resource, id, query }) {
return http.get(`${resource}/${id}`, {
params: query
})
}
create ({ resource, data, query }) {
return http.post(resource, data, {
params: query
})
}
update ({ resource, id, data, query }) {
return http.patch(`${resource}/${id}`, data, {
params: query
})
}
destroy ({ resource, id }) {
return http.delete(`${resource}/${id}`)
}
}
export default new APIProvider({
url: env('API_URL') // We assume 'https://api.example.com/v1' is set as the env variable
})
Next, in your main.js file or wherever else you bootstrap the Vue app, do the following:
import api from '#/src/utils/api'
Vue.$api = api
Object.defineProperty(Vue.prototype, '$api', {
get () {
return api
}
})
Now you can access it anywhere in your Vue app as well as anywhere you import Vue itself:
<template>
<div class="my-component">My Component</div
</template>
<script>
export default {
name: 'MyComponent',
data () {
return {
data: []
}
},
async created () {
const response = await this.$api.find({ resource: 'tasks', query: { page: 2 } })
this.data = response.data
}
}
</script>
or:
// actions.js from Vuex
import Vue from 'vue'
export async function fetchTasks ({ commit }) {
const response = await Vue.$api.find({ resource: 'tasks', query: { page: 2 } })
commit('SAVE_TASKS', response.data)
return response
}
Hope this helps.
I think for your simple question the answer could be any ES6 module containing functions (equivalent to methods in class in ANgular) and directly importing them in components using ES6 imports and exports. There are no such services that could be injected in components.
You can make your own service where you can place all your HTTP server calls and then import that to the components where you want to use them.
Best is to make use of Vuex for complex state management applications because in Vuex you are able to handle all async calls via actions which always run asynchronously and then commit the mutation once you have the result.Mutation will directly interact with the state and will update it in an immutable manner (which is preferred). This is stateful approach.
There are other approaches as well. But these are the ones which I follow in my code.

Ember - Sideloading data within RESTAPISerializer

I am trying to sideload data with RESTAPISerializer, but there seems to be some problem.
The goal is to store articles with tags. The articles appear in the store just fine, and I can look at them with Ember inspector. But I can't seem to figure out how to store the tags, they never show up in the inspector.
I created a model called blog-tag to store the tags. They come in a specific format, which I can't change, and it looks like this:
tid:term
... and I am getting an array of them with each article.
The article model includes a hasMany relationship with blog-tags. I am trying to pass them in through the serializer, and have been using various formats (JSON, javascript arrays, etc.) I wrote a custom serializer for blog tags as well, but it doesn't seem to do much.
Can someone explain what I am missing?
The article model:
import DS from 'ember-data';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
title: attr(),
blogTags: hasMany('blog-tag', { async: true })
});
The blog-tag model:
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
export default Model.extend({
tid: attr('number'),
term: attr('string'),
});
The article serializer:
import RESTAPISerializer from 'ember-data/serializers/rest';
export default RESTAPISerializer.extend({
normalizeResponse: function(store, primaryModelClass, payload, id, requestType) {
var normalizedPayload = {articles: []};
payload.forEach(function(item) {
var tags = item.tags.split("|"), blogTags = [];
// storing blog tags in an array, have also done it as a
// JSON structure
var blogTags = tags.map((tag) => {
var item = tag.split(":"),
vals = {};
vals.tid = item[0];
vals.term = item[1];
return vals;
});
var article = {
id: item.nid,
title: item.title,
blogTags: blogTags,
};
normalizedPayload.articles.push(article);
});
return this._super(store, primaryModelClass, normalizedPayload, id, requestType);
},
});
Solved my own problem. Answering the question for the benefit of anyone else running into the same issue.
What I was trying to do is embed the tags from the articles serializer. This mean I needed to use the EmbeddedRecordsMixin.
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
It was actually pretty simple to do this. Made the following changes to the serializer described above, the code worked as-is with these additions.
// app/serializers/articles.js
import RESTSerializer from 'ember-data/serializers/rest';
import EmbeddedRecordsMixin from 'ember-data/serializers/embedded-records-mixin';
export default RESTSerializer.extend(EmbeddedRecordsMixin, {
attrs: {
blogTags: { embedded: 'always' }
}
});