I have a bug, i'm trying to make his tutorial for twitter clone in nextjs+tailwindcss+typescript
https://www.youtube.com/watch?v=rCselwxbUgA&t=1357s&ab_channel=SonnySangha
1:42:05 / 3:17:52
I did exactly the same but i feel like my IDE or my nextJS version is making things different
import { Tweet } from "../typings"
export const fetchTweets = async () => {
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getTweets/`)
const data = await res.json();
const tweets: Tweet[] = data.tweets;
return tweets
}
FetchError: request to https://localhost:3000/api/getTweets/ failed,
reason: write EPROTO 140020696905664:error:1408F10B:SSL
routines:ssl3_get_record:wrong version
number:../deps/openssl/openssl/ssl/record/ssl3_record.c:332:
This error happened while generating the page. Any console logs >will be displayed in the terminal window.
import { Tweet } from "../typings"
export const fetchTweets = async () => {
if(global.window) {
const res = await
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getTweets/`)
const data = await res.json();
const tweets: Tweet[] = data.tweets;
return tweets
}
}
Server Error Error: Error serializing .tweets returned from
getServerSideProps in "/". Reason: undefined cannot be serialized
as JSON. Please use null or omit this value.
If someone can help me <3 thanks
FIXED :
.env.local
i writed
NEXT_PUBLIC_BASE_URL=https://localhost:3000/
change https:// by http:// and yarn run dev again
NEXT_PUBLIC_BASE_URL=http://localhost:3000/
I have been following a tutorial on youtube to build a twitter-clone website. However, when trying to fetch tweets from Sanity I am getting this error. I even git cloned the repo of the person that made the tutorial and I'm still getting the same error. This leads me to believe it is an issue with my VS code and not the code itself, if anyone has any suggestions that would be great thank you.
// fetchTweets.ts
export const fetchTweets = async () => {
const res = await fetch(`http://localhost:3001/api/getTweets`)
const data = await res?.json()
const tweets: Tweet[] = data.tweets
console.log('fetching', tweets)
return tweets
}
// index.tsx
export const getServerSideProps: GetServerSideProps = async (context) => {
const tweets: Tweet[] = await fetchTweets()
return {
props: {
tweets,
},
}
}
That error is typically caused by trying to render HTML as JSON—and particularly, when JSON is expected but instead an API returns an error page. Is your server definitely running on port 3001? Fetching from a non-existent server is likely consistent with this error.
I'm using the #prismicio/client library to fetch data from Prismic inside my Next.js app, but I can't figure out how to connect the preview mode and the preview ref it provides to the client.query call in order to fetch data for that specific timeline ref.
I tried looking for this in the technical reference for both the library itself and the guide on how to use Preview mode. They both mention I can use refs, but don't show an actual example. Here is my current set up, which is not working:
Inside my preview.ts page, I have this custom getServerSideProps function, which uses a custom call to my CMS handler where I pass the ref token:
export const getServerSideProps = async (context: NextPageContext): Promise<unknown> => {
const lang = getLangFromContext(context);
const { slug } = context.query as { slug: string[] };
const { token, documentId } = context.query;
if (!token) {
return console.warn(`No token available, check your configuration`);
}
const { pageComponents, meta } = await getCmsPage(slug ? slug.join("/") : "", lang, token);
return { props: { pageComponents, meta } };
};
When making the call to the Prismic API:
const client = Prismic.client(refApiUrl, {
accessToken: apiToken,
});
const res: any = await client.query(predicates, {
key: refToken,
});
This results in the server side error:
Error: Unknown field key
at SearchForm.set (C:\main\Sprybase\projects\prismic-integration\dist\node_modules\#prismicio\client\cjs\#prismicio\client.js:200:19)
at ResolvedApi.query (C:\main\Sprybase\projects\prismic-integration\dist\node_modules\#prismicio\client\cjs\#prismicio\client.js:606:25)
at C:\main\Sprybase\projects\prismic-integration\dist\node_modules\#prismicio\client\cjs\#prismicio\client.js:1164:63
at processTicksAndRejections (internal/process/task_queues.js:88:5)
What am I doing wrong? What is the correct way to pass a timeline ref to the Prismic client call?
It seems in your application you're not using the getPreviewResolver function or a linkresolver which the toolbar uses to get the correct page and load the preview cookie on top of the browser.
Is there any reason why you're doing your preview in this way?
Thanks.
I'm trying to create a blog page to test nextjs and created a dynamic route for the posts, which will be retrieved from Contentful. When navigating from Home page and clicking into a next/router <Link /> component, the blog post loads correctly, but if I get the URL and try loading the page directly from browser address bar, I'll get 404.
Steps to reproduce:
1. git clone https://github.com/zeit/next-learn-demo.git
2. cd next-learn-demo/8-deploying
3. yarn
4. next build && next export
5. cd out
6. serve
7. Navigate to http://localhost:5000/p/learn-nextjs
8. See 404
Is this a limitation of NextJS (didn't find anything related to it on documentation) or do we need to configure anything else?
The real issue is that exporting a next app will make it generate static HTML files. Even though it will still be able to request data before rendering the page, the set of available paths are not dynamic (they are generated during the next export command). See this docs and this example.
Based on this, I have 2 possible solutions:
generate a webhook to trigger a next build && next export command every time a new blog post is published in Contentful;
avoid exporting my next app and host a Node server that will handle the dynamic routes.
That's because when you directly access the link or refresh the page then it add's a slash at the end of route. An next.js doesn't recognize any route like that. To fix this, I hope there should be an easiest way to do that. However you can do this using custom server. Here is an example:
server.get("/about/", (req, res) => {
return app.render(req, res, "/about")
});
Hope this will help you.
To extend the answer provided by #Minoru, the official Next documentation covers this case in this example.
Using getStaticPaths and getStaticProps allows you to create dynamic pages at build time, avoiding the 404.
Example code for posts dynamic page:
import { GetStaticPaths, GetStaticProps } from 'next';
const Post = ({ post }) => {
const { id, content } = post;
return (
<div>
<h1>Post {id}</h1>
<p>{content}</p>
</div>
);
};
export const getStaticPaths: GetStaticPaths = async () => {
// Get all posts via API, file, etc.
const posts = [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }, { id: '5' }]; // Example
const paths = posts.map(post => ({
params: { id: post.id },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps = async context => {
const postId = context.params?.id || '';
// Get post detail via API, file, etc.
const post = { id: postId, content: `I'm the post with id ${postId}!` }; // Example
return { props: { post } };
};
export default Post;
When building the site with next build && next export we will see in the out folder that Next has created each post page
Then, when you navigate to /posts/3/ you will see the post with id 3
For reference, this docs page contains this case and many other use cases.
Don't want to infringe any old posts rules, but in case anyone else in my context I use vercel's feature webhook to new deploys and as I was using firebase I've created a simple firebase function whith is hooked to a new event creation of a page triggers the webhook. I've used fetch because we can make a GET request according to the docs
exports.newEventAdded = functions.region('us-central1').firestore.document('local_events/{localeventId}')
.onCreate((snap, context) => {
fetch('https://api.vercel.com/v1/integrations/deploy/process.env.NEXT_PUBLIC_VERCEL_WEBHOOK_ID')
.then(function (response) {
return response.json();
})
.then(function (myJson) {
console.log(JSON.stringify(myJson));
});
})
I'm using express as my custom server for next.js. Everything is fine, when I click the products to the list of products
Step 1: I click the product Link
Step 2: It will show the products in the database.
However if I refresh the /products page, I will get this Error
Server code (Look at /products endpoint)
app.prepare()
.then(() => {
const server = express()
// This is the endpoints for products
server.get('/api/products', (req, res, next) => {
// Im using Mongoose to return the data from the database
Product.find({}, (err, products) => {
res.send(products)
})
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
Pages - products.js (Simple layout that will loop the products json data)
import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'
const Products = (props) => (
<Layout>
<h1>List of Products</h1>
<ul>
{ props.products.map((product) => (
<li key={product._id}>{ product.title }</li>
))}
</ul>
</Layout>
)
Products.getInitialProps = async function() {
const res = await fetch('/api/products')
const data = await res.json()
console.log(data)
console.log(`Showed data fetched. Count ${data.length}`)
return {
products: data
}
}
export default Products
As the error states, you will have to use an absolute URL for the fetch you're making. I'm assuming it has something to do with the different environments (client & server) on which your code can be executed. Relative URLs are just not explicit & reliable enough in this case.
One way to solve this would be to just hardcode the server address into your fetch request, another to set up a config module that reacts to your environment:
/config/index.js
const dev = process.env.NODE_ENV !== 'production';
export const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';
products.js
import { server } from '../config';
// ...
Products.getInitialProps = async function() {
const res = await fetch(`${server}/api/products`)
const data = await res.json()
console.log(data)
console.log(`Showed data fetched. Count ${data.length}`)
return {
products: data
}
}
Similar to the #Shanker's answer, but if you prefer not to install the additional package for this, here is how to do it.
async getInitialProps({ req }) {
const protocol = req.headers['x-forwarded-proto'] || 'http'
const baseUrl = req ? `${protocol}://${req.headers.host}` : ''
const res = await fetch(baseUrl + '/api/products')
}
It sounds silly but worth mentioning. If you're using SSR in your webapp the fetch call will work with a relative link on the client but will fail on the server. Only the server needs an absolute link!
If you want to prevent the server from making the request just wrap it in logic
if(global.window){
const req = fetch('/api/test');
...
}
You could utilize environment variables if your project is hosted on a provider that supports it.
env.local
// Local
URL="http://localhost:3000"
// Production
URL="https://prod.com"
Then you can use the following.
const { URL } = process.env;
const data = await fetcher(URL + '/api');
This simple solution worked for me without having to add an additional config file,
Install
npm install --save next-absolute-url
Usage
import absoluteUrl from "next-absolute-url";
async getInitialProps({ req }){
const { origin } = absoluteUrl(req, req.headers.host);
console.log('Requested URL ->',origin);
// (or) other way
const host = absoluteUrl(req, req.headers.host);
console.log('Requested URL ->',host.origin);
}
Case 1. It's not an error. The isomorphic-unfetch is running by SSR mode, so Node.js needs to know the absolute url to fetch from it, because the back-end doesn't know your browser settings.
Case 2. Another scenario is to prevent the http host poisoning headers attack.
append secret keys and tokens to links containing it:
<a href="http://_SERVER['HOST']?token=topsecret"> (Django, Gallery, others)
....and even directly import scripts from it:
<script src="http://_SERVER['HOST']/misc/jquery.js?v=1.4.4">
Case 3. The isomorphic-unfetch it's the library we are going to use to fetch data. It's a simple implementation of the browser fetch API, but works both in client and server environments.
Read more about it:
Isomorphic unfetch - Switches between unfetch & node-fetch for client & server
Prevent http host headers attack
Fetching Data for Pages
In the NextJS 9.5, we can also use process.cwd().
process.cwd() will give you the directory where Next.js is being executed.
import path from 'path'
import fs from "fs";
export const getStaticProps: GetStaticProps = async () => {
const dataFilePath = path.join(process.cwd(), "jsonFiles", "data.json");
console.log(dataFilePath); // will be YourProject/jsonFiles/data.json
const fileContents = fs.readFileSync(dataFilePath, "utf8");
const data: TypeOfData[] = JSON.parse(fileContents);
return { props: { data } };
};
Ref: https://nextjs.org/docs/basic-features/data-fetching#reading-files-use-processcwd
Putting this out there because this showed up in google results for my problem, even though the question itself isn't really related (outside of the fact that the same dependency is throwing the same error message, albeit in a different context for a different reason).
I got this issue from using hardhat while attempting to verify (verify:verify) my contract on etherscan. The problem was that in the hardhat config, I didn't have a full url under rinkeby (since I was verifying on rinkeby, would be mainnet, etc.). Copy/pasting some config stuff quickly into a project I cloned from someone else, they had a full URL in their .env, while I had the url in the config and stored only my api key in my .env.
To figure this out, though, was straightforward--go into node_modules, then find the node-fetch folder, then lib, (this is from memory--just find the line that is vomitting in your stack trace) then the line number, and put a console log there to see what the "bad" url you're seeing is. Usually that's enough of a clue; in my case, it was an API key, obviously not a URL, and that made it straightforward to solve.
If you have an absolute path issues. Try to use swr to access data.
Notice: This is a React hooks so you must call inside the component.
import useSWR from 'swr';
// optionally you can use unfetch package from npm or built yours to handle promise.
const fetcher = (...args) => fetch(...args).then(res => res.json())
export const AllProducts = () => {
const { data, error } = useSWR('/api/products', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return (
<div>{data}</div>
);
};
Export or deploying in production
Whenever you are trying to deploy on Vercel you might encounter an error. For instance `
warn - Statically exporting a Next.js application via `next export` disables API routes`.
It means you are trying to export data and NextJS does not support fetching data from pages/api/* directory. To avoid errors, its better to separate build and export command.
// package.json
{
"scripts": {
"dev": "next",
"build": "next build", // No next export command
"start": "next start"
},
}
Thanks folks for great contribution and I hope the answer shared will help somebody too.
Make sure what the value of your API url is
In my case, I was using POST but my url was somewhat undefined.
Use console.log to see where is your request going.
this is a way to get the base hostname to fetch data from external endpoint
without getting that error
function return_url(context) {
if (process.env.NODE_ENV === "production") {
return `https://${context.req.rawHeaders[1]}`;
} else if (process.env.NODE_ENV !== "production") {
return "http://localhost:3000";
}
}
and on the getServerSideProps or getStaticProps functions you use
export async function getServerSideProps(context) {
let url = return_url(context);
const data = await fetch(`${url}/yourEndPoint`).then((res) => res.json());
return {
props: {
data: data,
},
};
}
If you are using next environment config prefix your variables with NEXT_PUBLIC_ as mentioned here Exposing Environment Variables to the Browser.
USE: NEXT_PUBLIC_STRAPI_URL="http://localhost:1337" instead of
NEXT_PUBLIC_STRAPI_URL=http://localhost:1337
use .log(console.log) after nock , so you will get exact unmatched and expected url .
Example:
nock("http://localhost")
.log(console.log)
.persist()
.get("/api/config")
.reply(200, { data: 1234 })