Unable to get Cloudflare GraphQL from NuxtJS with Apollo - graphql

I have been tasked to create a widget to get specific traffic from GraphQL provided by Cloudflare using NuxtJS2 with Apollo, so far I am not making any progress, and cannot get anyway forward - I am going loopy.
apollo: {
connectToDevTools: true,
clients: {
connectToDevTools: true,
default: {
connectToDevTools: true,
httpEndpoint: 'https://api.cloudflare.com/client/v4/graphql',
headers: {
'x-auth-email': 'email#mail.com',
'authorization': 'Bearer some_token'
}
}
}
},
The above is my Apollo configuration in nuxt.config.js - which I believe is correct, to satisy the requirements of the Cloudflare GraphQL endpoint, as outlined here in their documentation regarding such:
https://developers.cloudflare.com/analytics/graphql-api/getting-started/authentication/graphql-client-headers/
In my frontend component,
<template>
<div>
<ul v-if="analytics">
<li v-for="item in analytics.viewer.zones[0].firewallEventsAdaptiveGroups" :key="item.dimensions.clientIP">
<p>{{ item.dimensions.clientIP }}</p>
<p>{{ item.dimensions.clientCountryName }}</p>
<p>{{ item.sums.bytes }}</p>
<p>{{ item.sums.httpRequests }}</p>
</li>
</ul>
<p v-else>Loading...</p>
</div>
</template>
<script>
import gql from 'graphql-tag'
export default {
name: 'IndexPage',
layout: 'index',
apollo: {
analytics: gql `
query {
viewer {
zones(filter: {
zoneTag: "ourZoneId"
}) {
firewallEventsAdaptiveGroups(filter: {
hostname: "ourdomain.io"
}, limit: 100) {
dimensions {
clientIP
clientCountryName
}
sums {
bytes
httpRequests
}
}
}
}
}
`
}
}
</script>
Which I believe is correct, however, nothing is loading and we're unable to use the Apollo debug tool to actually figure it out, so I am at a loss, the error I get on the frontend is:
Cannot read properties of undefined (reading 'viewer')
I believe the error is returned due to the fact nothing is being returned from Apollo, but I am not sure and I am at a complete loss! Any input would be greatly appreciated! :)

Related

using axios data from Laravel pagination array

ok I am stumped and I know its going to be something stupid. So let me explain more in detail. The code above is what I believe should work for pagination from a Laravel controller to a vue component. I get that I am not fully accounting for pagination and I can handle that later but the first results are not available for use when I do this and I do not understand where my syntax is wrong.
Vue:
<ul role="list" class="space-y-4">
<li
v-for="item in allActivities"
:key="item.id"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
Mounted:
mounted() {
axios
.get("/activity")
.then((response) => (this.allActivities = response.data));
},
Controller
public function index()
{
$activity = Activity::paginate(10);
return $activity;
}
If in the v-if I change it to allActivities.data it refreshes to show the data until I reload the page and get id not found.
If I change the axios to response.data.data it works, but I lose pagination.
IM stuck
response.data is the result from laravel
response.data.data if the data key in the result which is a list of your models
response.data will contain these keys if using
current_page
data
first_page_url
from
last_page
last_page_url
links
next_page_url
path
per_page
prev_page_url
to
total
I did not fully understand the problem. If you want to change page, send the value of the page you want to display to the fetchActivities() method.
EDIT!
<script>
export default {
data() {
return {
allActivities: {
data: [],
total: null
}
}
},
methods: {
async fetchActivities(page = 1){
const {data:activities} = await axios.get(`/activity?page=${page}`)
this.allActivities.data.push(activities.data)
this.allActivities.total = activities.total
}
},
created() {
this.fetchActivities()
}
}
</script>
<template>
<div v-if="allActivities.data.length > 0">
<ul role="list" class="space-y-4">
<li
v-for="(item, index) in allActivities.data"
:key="index"
class="bg-gray-800 px-4 py-6 shadow sm:p-6 sm:rounded-lg"
>
{{ item.description }}
</li>
</ul>
</div>
</template>

Laravel vue.js and vuex link body text by id and show in a new component

I am very new to Laravel and Vuex, I have a simple array of post on my page.
test 1
test 2
test 3
I am trying to link the text on the AppPost.vue component and show the post that has been clicked on a new component (AppShowpost.vue) on the same page. I believe I have to get the post by id and change the state? any help would be good. Thank you.
when you click test 1 it will show "test 1" on a new component (AppShowpost.vue)
In side my store timeline.js, I belive I need to get the post by id and change the state ?
import axios from 'axios'
export default {
namespaced: true,
state: {
posts: []
},
getters: {
posts (state) {
return state.posts
}
},
mutations: {
PUSH_POSTS (state, data) {
state.posts.push(...data)
}
},
actions: {
async getPosts ({ commit }) {
let response = await axios.get('timeline')
commit('PUSH_POSTS', response.data.data)
}
}
}
My AppTimeline.vue component
<template>
<div>
<app-post
v-for="post in posts"
:key="post.id"
:post="post"
/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters({
posts: 'timeline/posts'
})
},
methods: {
...mapActions({
getPosts: 'timeline/getPosts'
})
},
mounted () {
this.getPosts()
}
}
</script>
My AppPost.vue component. I need to link the post.body to display the post in my AppShowpost.vue component.
<template>
<div class="w-full inline-block p-4">
<div class="flex w-full">
<p>
{{ post.body }}
</p>
</div>
</div>
</template>
<script>
export default {
props: {
post: {
required: true,
type: Object
}
}
}
</script>
My AppSowpost.vue component that needs to display the post that is clicked.
<template>
<div>
// Displaypost ?
</div>
</template>
<script>
export default {
// Get post from id ?
}
</script>
Okay you can create a new state in your vuex "current_state", asyou said, you can dispatch a mutation by passing the id to the vuex.
state: {
posts: [],
current_state_id : null
},
In your mutations
set_state_id (state, data) {
state.current_state_id = data;
}
On your app post.vue, you can set a computed property that watches the current state
computed: {
currentState() {
return this.$store.getters["timeline/current_state_id"];
}}
And create a watcher for the computed property to display the current id/post
watch: {
currentState: function(val) {
console.log(val);
},
Maybe this will help you. First I will recommend to use router-link. Read about router link here if your interested. It is very helpful and easy to use. But you will have to define the url and pass parameter on our vue-route(see bellow).
1.You can wrap your post.body in router-link as follow.
//With this approach, you don't need any function in methods
<router-link :to="'/posts/show/' + post.id">
{{ post.body }}
</router-link>
2. In your AppSowpost.vue component, you can find the post in vuex state based on url params as follow.
<template>
<div> {{ thisPost.body }} </div>
</template>
// ****************
computed: {
...mapState({ posts: state=>state.posts }),
// Let's get our single post with the help of url parameter passed on our url
thisPost() { return this.posts.find(p => p.id == this.$route.params.id) || {}; }
},
mounted() { this.$store.dispatch("getPosts");}
3. Let's define our vue route.
path: "posts/show/:id",
name: "showpost",
params: true, // Make sure the params is set to true
component: () => import("#/Components/AppShowPost.vue"),
Your Mutations should look as simple as this.
mutations: {
PUSH_POSTS (state, data) {
state.posts = data;
}
},
Please let me know how it goes.

Loading JSON data in Datatable takes too long

I'm using Laravel and Vue for a project I'm working on. Within my dashboard I'm using BootstrapVue's datatable named B-table, which receives JSON data from my API.
The API currently only returns 1 user, however it takes quite some time to load it even though it's just 1 row. I created this GIF to show you it's loading time when refreshing the webpage:
I'm using Axios to receive data from my API, I'm very curious what's causing it to be this slow. Here is my code:
<template>
<div>
<div id="main-wrapper" class="container">
<div class="row">
<div class="col-md-12">
<hr>
<b-table busy.sync="true" show-empty striped hover responsive :items="users" :fields="fields" :filter="filter" :current-page="currentPage" :per-page="perPage" #refreshed="verfris">
<template slot="actions" slot-scope="data">
<a class="icon" href="#"><i class="fas fa-eye"></i></a>
<a class="icon" href="#"><i class="fas fa-pencil-alt"></i></a>
<a class="icon"><i class="fas fa-trash"></i></a>
</template>
</b-table>
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0 pagination-sm" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: [],
filter: null,
currentPage: 1,
perPage: 10,
totalRows: null,
selectedID: null,
fields: [
{
key: 'id',
sortable: true
},
{
key: 'name',
sortable: true
},
{
key: 'email',
sortable: true
},
{
key: 'actions'
}
],
}
},
mounted() {
this.getResults();
},
methods: {
// Our method to GET results from a Laravel endpoint
getResults(ctx, callback) {
axios.get('/api/users')
.then(response => {
this.users = response.data;
this.totalRows = response.data.length;
return this.users;
});
}
},
}
</script>
And the JSON data my API returns:
[
{
"id": 1,
"name": "test",
"email": "user#user.nl",
"email_verified_at": null,
"created_at": "2018-09-28 16:04:36",
"updated_at": "2018-09-28 16:04:36"
}
]
What can I do to solve this loadtime issue?
UPDATE: I'm hosting it on Ubuntu within VirtualBox, maybe it would be important to any of you guys.
UPDATE response time of the request to my API:
From your comment in the post, I guess you already figured out what wrong? i.e. Something is funky about your VirtualBox setup?
I cloned your app and it only took 88ms to load on my Mac.
BTW, your code does not run out of the box. Had to fix and bypass a couple things to get it to run. I would also suggest you install laravel-debugbar to help with debug in laravel.

Laravel router-link works only the first time

I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.

Using Vue in Laravel project

working on a project in Laravel and I want to integrate Vue and Vue Resource into it. I have setup it up but it is not working as expected. Below is my code:
routes.php
Route::get('api/projects', function() {
return App\Project::all();
});
app.js
new Vue({
el: '#project',
data: {
projects: []
},
ready: function() {
this.getProjects();
},
methods: {
getProjects: function() {
this.$http.get('api/projects').then(function(projects) {
this.$set('projects', projects);
});
}
}
});
my view
<div class="content" v-for="project in projects">
<h3 class="title">
#{{ project.title }}
</h3>
#{{ project.description }}
</div>
With the code above, nothing is displayed on the page but if I
#{{ $data | json }}
I get projects data in json. This is kind of weird, please what am I doing wrong.
Thanks to #PROGRAMMATOR on laracast. His answer solved the issue.
this.$set('projects', projects.data);
I saw you are using code like this :
this.$http.get('api/projects').then(function(projects) {
this.$set('projects', projects);
});
But you have to bind this with $http request as right now this.$http working within method :
this.$http.get('api/projects').then(function(projects) {
this.$set('projects', projects);
}.bind(this));
You can use like this also :
this.projects = projects;
then :
{{ $data | json }}
if result coming along with .data array then
this.projects = projects.data;

Resources