I'd like to generate dynamic url fo each slug, but there is an array only with pages which I declared: const pages = ["/", "/about", "/portfolio", "/blog"];
http://localhost:3000/api/my-sitemap. I've installed npm sitemap from https://www.npmjs.com/package/sitemap
my query in ../../lib/data.js
export const getBlogSlugs = async () => {
const endpoint =
"https://api-eu-central-gsagasgasxasasxsaxasxassaster";
const graphQLClient = new GraphQLClient(endpoint);
const query = gql`
{
posts {
slug
}
}
`;
return await graphQLClient.request(query);
};
pages/api/my-sitemap.js
import { getBlogSlugs } from "../../lib/data";
const { SitemapStream, streamToPromise } = require("sitemap");
const { Readable } = require("stream");
export const getStaticProps = async () => {
const { posts } = await getBlogSlugs();
return {
props: {
posts,
},
};
};
export default async (req, res, posts) => {
try {
const links = [];
posts?.map((slug) => {
links.push({
url: `/blog/${slug}`,
changefreq: "daily",
priority: 0.9,
});
});
// Add other pages
const pages = ["/", "/about", "/portfolio", "/blog"];
pages.map((url) => {
links.push({
url,
changefreq: "daily",
priority: 0.9,
});
});
// Create a stream to write to
const stream = new SitemapStream({
hostname: `https://${req.headers.host}`,
});
res.writeHead(200, {
"Content-Type": "application/xml",
});
const xmlString = await streamToPromise(
Readable.from(links).pipe(stream)
).then((data) => data.toString());
res.end(xmlString);
} catch (e) {
console.log(e);
res.send(JSON.stringify(e));
}
};
I added to my robots.txt in pudblic folder:
User-agent: *
Allow: /
Sitemap: http://localhost:3000/api/my-sitemap
What I got is only declared pages
localhost:3000/api/my-sitemap
I tried like this and doesn't work too:
export const getStaticProps = async () => {
const data = await getBlogSlugs();
return {
props: {
posts: data.posts,
},
};
};
export default async (req, res, posts) => {
try {
const links = [];
posts?.map((post) => {
links.push({
url: `/blog/${post.slug}`,
changefreq: "daily",
priority: 0.9,
});
});
You cannot use getStaticProps from an API route.
https://github.com/vercel/next.js/discussions/16068#discussioncomment-703870
You can fetch the data directly inside the API function.
Edit: In my app, I use the API route code below to fetch data from external server
import fetch from "isomorphic-unfetch";
export default async (req, res) => {
try {
const result = await fetch("YOUR_URL");
const posts = await result.json();
//use posts
});
} catch (e) {}
};
For GraphQl may be you can check the example given in vercel site
https://github.com/vercel/next.js/tree/canary/examples/api-routes-graphql
Related
I use Next JS in my project. I want do only one request in page. And in next docs says that using get Static Props is that's what I need. But in doesnt work for me.
This is my code
export async function getStaticPaths() {
const leaguesCol = await collection(database, 'leagues');
const leaguesSnapshot = await getDocs(leaguesCol);
const leagues = leaguesSnapshot.docs.map(doc => doc.data());
return {
paths: leagues.map((item) => ({
params: {
id: item.link,
},
})),
fallback: false,
};
}
export async function getStaticProps() {
const { id } = context.params;
const leaguesRef = await collection(database, 'highlights');
const q = query(leaguesRef, where('league', '==', id));
const leagueSnapshot = await getDocs(q);
const data = leagueSnapshot.docs.map(doc => doc.data());
return {
props: { data },
};
}
But, when i deploy project in Firebase, i see that request happens in every routing to page. For example, in screen my routing between "spain" and "germany" pages
enter image description here
I was working on a project using Firebase cloud messaging react. I was sending this to my server, but it doesn't work. Surely I have tried, but I don't know what's wrong again.
Below is the code.
Here it sends a POST request to Firebase, and it should send a notification to the user.
async function sendNotification(id, userMessage) {
const headers = {
'Authorization': `key=${code}`,
'Content-Type': 'application/json'
}
const message = {
'to': `${id}`,
'content_available': true,
'apns_priority': 5,
'notification': {
body: `${userMessage}`
},
const url = 'https://fcm.googleapis.com/fcm/send'
//console.log(code)
await axios.post(url, message, {
headers: headers
})
}
const sendMessageToServer = async (e) => {
//e.preventDefault();
toggle()
const docRe = doc(database, "help", mailer);
const data = {
email: user.email,
user: newMessage,
}
//console.log(data, 'not clear')
setNewMessage('')
//console.log(data, newMessage, 'cleared')
setShow(false)
if(newMessage === '') {
}
else {
const docRef = doc(database, "users", mailer);
await updateDoc(docRe, {
msg: arrayUnion(data)
})
.then(() => {
async function p() {
const id = await getDoc(docRef)
//console.log(id.data())
sendNotification(id.data().notice, `Admin : ${data.user}`)
}
p()
})
}
Sometimes it sends to my localhost because I tested there, but it doesn't work on my Netlify app. Secondly, I noticed that it keeps generating the same token for each user, but that's not the issue, but if you can help in both I would be grateful.
export default function Dashboard() {
async function callToken() {
await getToken(messaging, {vapidKey: process.env.NOTIFICATION})
.then((code) => {
//console.log(code)
async function docRef() {
const dc = doc(database, "users", auth.currentUser.email);
await updateDoc(dc, {
notice: code
});
}
docRef()
})
}
async function requestPermission() {
await Notification.requestPermission()
.then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.')
callToken()
}
})
}
const goTo = useNavigate();
useEffect(() => {
onAuthStateChanged(auth, (data) => {
if(!data) {
goTo('/login')
}
else {
currentBalance();
requestPermission()
}
})
})
}
Please know I imported all required modules.
I'm building out a new marketing site for my company using next.js, and I'm running into an issues with URLS. Essentially, I've built a custom API route to access data from our internal database, using Prisma:
getAllDealers.ts
import Cors from 'cors';
import { prisma } from 'lib/prisma';
import { NextApiResponse, NextApiRequest, NextApiHandler } from 'next';
const cors = Cors({
methods: ['GET', 'HEAD'],
});
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: any) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
const getDealers: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse) => {
const { method } = req;
await runMiddleware(req, res, cors);
const dealers = await prisma.crm_dealers.findMany({
where: {
active: {
not: 0,
},
},
});
switch (method) {
case 'GET':
res.status(200).send({ dealers, method: method });
break;
case 'PUT':
res.status(500).json({ message: 'sorry, we only accept GET requests', method: method });
break;
default:
res.setHeader('Allow', ['GET']);
res.status(405).end(`Method ${method} Not Allowed`);
}
};
export default getDealers;
And I've built a route to access individual dealers:
getSingleDealer.ts
import Cors from 'cors';
import { prisma } from 'lib/prisma';
import { NextApiResponse, NextApiRequest, NextApiHandler } from 'next';
const cors = Cors({
methods: ['GET', 'HEAD'],
});
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: any) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
const getDealerById: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse) => {
await runMiddleware(req, res, cors);
const dealer = await prisma.crm_dealers.findUnique({
where: {
id: Number(req.query.id),
},
});
res.status(200).send({ dealer, method: req.method });
};
export default getDealerById;
I can use my getSingleDealer function in getServerSideProps like so:
export const getServerSideProps = async ({ params }: Params) => {
const { uid } = params;
const { dealer } = await getSingleDealer('api/dealer', uid);
return {
props: { dealer },
};
};
And this works just fine. What I need to do though is prettify my URLS. Right now, the way to access a singular dealer's page is dealers/1 with 1 being whatever the ID of the dealer is. I want to have that URL be a string, like dealers/sacramento-ca (that location is also served up in the API) while still accessing the API on the basis of the id so it's searching for an integer, rather than a string. Is that possible within next?
You'd handle the routing in your client with getServerSideProps similarly to what you are doing now. To do so, you need to configure your dynamic routing file or folder name to match your desired format.
Example folder structures are:
pages > dealers > [dealer].tsx = /dealers/sacramento-ca
pages > dealers > [location] > index.tsx = /dealers/sacramento-ca
export const getServerSideProps = async ({ params }: Params) => {
const { uid } = params;
const { dealer } = await getSingleDealer('api/dealer', uid);
if (!dealer ) {
return { notFound: true }
}
return {
props: {
...dealer,
location: 'sacramento-ca', // object key must match your dynamic [folder or file's name]
},
};
};
All dynamic URL parts must be included as a key in the return.
pages > dealers > [state] > index.tsx [city].tsx = /dealers/ca/sacramento
return {
props: {
...dealer,
city: 'sacramento',
state: 'ca',
},
};
Here is a article detailing what you will need to do. It's important to note that sometimes it's desirable to use a catch all route to simplify searching and deeply nested dynamic routes.
I'm building an App with Next.js, and I need to connect to specific API routes (set up with API Platform) and populate pages with the route's responses.
The API is working fine, but no matter how I try to implement my Axios call inside the getServerSideProps, I always get the same error, ECONNREFUSED, from my Node stack.
I tried to get the data from useEffect() and it's working fine, but I would like to know if there's a way to call it directly in getServerSideProps.
I'm using a Node container for Docker, and the routes are authenticated through a JWT Token (stored in the session and the client cookies for the server-side connection)
Here are is my code:
pages/accounts.js:
export async function getServerSideProps(context) {
const cookies = new Cookies(context.req.headers.cookie)
const adminToken = cookies.get('jwtToken')
const res = await getAllAccounts(adminToken)
return {
props: {
testdata: ''
},
}
}
lib/accounts.js:
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`).then((response) => {
}).catch((error) => {
console.dir(error)
})
}
HTTP wrapper:
import axios from 'axios';
import jwt_decode from "jwt-decode";
import mockAdapter from 'axios-mock-adapter';
const service = ({ jwtToken = null, store = null, mockURL = null, mockResponse = null, multipart = false } = {}) => {
const options = {};
options.baseURL = process.env.NEXT_PUBLIC_API_URL + '/api';
if(multipart === true) {
options.headers = {
'Content-Type': 'multipart/form-data'
}
} else {
options.headers = {
'Content-Type': 'application/ld+json',
accept: 'application/ld+json'
}
}
const instance = axios.create(options);
instance.interceptors.response.use(response => {
return response;
}, error => {
return Promise.reject(error);
})
if (mockURL !== null && mockResponse !== null) {
let mock = new mockAdapter(instance);
mock.onAny(mockURL).reply(200, mockResponse)
}
return instance;
};
export default service;
Through the error dump in the node stack, I managed to see that the request headers are correct, and the JWT correctly passed through.
Do not use Axios. Just use fetch().
Next.js polyfills fetch() by default on both the client and server, so you can just use it:
In addition to fetch() on the client-side, Next.js polyfills fetch() in the Node.js environment. You can use fetch() in your server code (such as getStaticProps/getServerSideProps) without using polyfills such as isomorphic-unfetch or node-fetch.
Source.
getServerSideProps works well with axios if we return response.data
export const getServerSideProps: GetStaticProps = async ({ params }) => {
const { brandName } = params as IParams;
const brandData = await $host.get(`api/brand/${brandName}`).then(response => response.data);
return {
props: {
brand: brandData,
},
};
};
Your problem is that your async method does not return a promise.
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`);
return res;
}
In my NextJS begining I followed this tutorial , and I changed fetch to axios in this way:
export const getStaticPaths = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
const paths = data.map((ninja) => {
return {
params: { id: ninja.id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const data = await res.json();
return {
props: { ninja: data },
};
};
I applied the change using useEffect()
useEffect(() => {
// const data = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// const res = await data.json();
// setninja(res);
const fetchData = async () => {
const result = await axios(`https://jsonplaceholder.typicode.com/users/${id}`);
setninja(result.data);
};
fetchData();
console.log(data);
}, []);
I hope this info will be useful for you.
I Used Axios in getServerSideProps without any problems.
export const getServerSideProps: GetServerSideProps = async({
params,
res
}) => {
try {
const response = await axios.get(`/api/test`);
return {
props: {
data: response.data
},
}
} catch {
res.statusCode = 404;
return {
props: {}
};
}
};
I am an aspiring react developer and I am having some issues with my app.
I am trying to scrape the New York times for an assignment I have, and I can get my data with a search to log server-side, but I cant seem to pass it back. I can get my data by pushing it to a MongoDB then querying it in a separate process from the front end, but I don't want to do that.
I want to pass the object back up the stack to the client side. Does anyone know how I might accomplish that?
here is some of my code.
my dir structure:
here is the client folder structure:
here is my Home.jsx file clientside in /pages:
import React, { Component } from 'react';
import { Container, Row, Column } from '../../components/BootstrapGrid';
import API from '../../utils/API'
import {Input, FormBtn} from '../../components/Form'
class Home extends Component {
state = {
formInput: "",
posts: [],
}
loadArticles = (res) => {
console.log('res')
}
handleInputChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
});
};
handleFormSubmit = event => {
event.preventDefault();
let query = this.state.formInput
// console.log(query)
API.scrapeArticles(query)
// console.log(this.state)
};
render() {
return (
<Container>
<Row>
<Column>
</Column>
<Column>
<Input
value={this.state.formInput}
onChange={this.handleInputChange}
name="formInput"
placeholder="Search Query (required)"
/>
<FormBtn onClick={this.handleFormSubmit}>Scrape NYT API</FormBtn>
</Column>
</Row>
</Container>
);
}
}
export default Home;
here is my code calling the clientside api in client/utils/api/:
import axios from "axios";
export default {
// getPosts: function () {
// return axios.get('/api/posts')
// },
// savePost: function (postData) {
// return axios.post("/api/posts", postData);
// },
scrapeArticles: function (query) {
// console.log(query)
let queryData = {
query: query
}
return axios.post('/api/scraper', queryData)
}
};
here is my code from the backend routes/index.js being hit by axios (i think? Im honestly not sure how but i think this is the flow):
const path = require("path");
const router = require("express").Router();
const postsController = require('../controllers/postsController')
router.route("/")
.get(postsController.getAll)
.post(postsController.create);
router.route('/api/scraper')
.post(postsController.scraper)
.get(postsController.scraper)
// If no API routes are hit, send the React app
router.use(function (req, res) {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
module.exports = router;
here is my controller that is referenced in the file above:
const scraper = require('../scraper')
const db = require('../models');
module.exports = {
create: function (req, res) {
db.Post
.create(req.body)
.then(dbmodel => res.json(dbmodel))
.catch(err => res.status(422).json(err))
},
getAll: function (req, res) {
db.Post
.find(req.query)
.sort({date: -1})
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err))
},
scraper: function (req, res) {
let queryData = req.body.query
scraper(queryData)
},
scraperGet: function (req, res) {
scraper()
console.log(res.body)
}
}
and lastly, here is the scraper file on the backend:
const request = require('request');
const mongoose = require('mongoose');
const db = require('./models');
const scraper = (queryData) => {
console.log(`#scraper ${queryData}`)
let articleData = []
request.get({
url: "https://api.nytimes.com/svc/search/v2/articlesearch.json",
qs: {
'api-key': "-----------------------------",
"q" : queryData
},
}, function (err, response, body) {
body = JSON.parse(body);
let articles = body.response.docs
articles.forEach(element => {
// console.log(element)
let title= element.headline.main
let url = element.web_url
let synopsis = element.abstract
let snippet = element.snippet
let source = element.source
let pubDate = element.pub_date
let article = {
title: title,
url: url,
synopsis: synopsis,
snippet: snippet,
source: source,
pubDate: pubDate,
}
// console.log(article)
articleData.push(article)
db.Post.create({title:article.title}).then(article => {
console.log(article)
}).catch(err => {
console.log(err)
})
});
return articleData
});
}
module.exports = scraper;
So i know right now it is pushing to mongo. This is only because I couldn't figure out how to pass that data back just stored in a variable.
I really don't want to have to push all my results to the db and then make a query for them. I want to have a save article function that you only save the ones you actually want.
You should send articleData to the client and then get it in the client side using .then() method of a promise.
Something like this:
scraper: function (req, res) {
let queryData = req.body.query
const articleData = scraper(queryData)
// return your json to the client
res.json(articleData)
},
Then you should receive this data in the client side, like this:
handleFormSubmit = event => {
event.preventDefault()
let query = this.state.formInput
API.scrapeArticles(query)
.then(resp => {
this.setState({ posts: resp })
})
}