TroubleShooting SearchKit for ElasticSearch - javascript

I am trying to connect searchkit to elasticSearch. I have been following the docs at https://www.searchkit.co/examples/tutorials to get this running on next.js.
I have a search.js file in the root directory that looks like this:
import { InstantSearch, SearchBox, Hits } from "react-instantsearch-dom";
import createClient from "#searchkit/instantsearch-client";
const searchClient = createClient({
url: "/api/search",
});
export default function Search() {
return (
<InstantSearch searchClient={searchClient} indexName="search_index">
<SearchBox />
<Hits />
</InstantSearch>
);
}
I have another search.js file in my pages/api directory that looks like this:
import Client from "#searchkit/api"
const apiConfig = {
connection: {
host: "http://localhost:9200/",
},
search_settings: {
highlight_attributes: ["title", "text"],
search_attributes: ["title", "text"],
result_attributes: ["title", "text"]
}
} ;
const apiClient = Client(apiConfig) ;
export default async function handler(req, res) {
console.log("BODY" + JSON.stringify(req.body))
const results = await apiClient.handleRequest(req.body) ;
// console.log("RESULTS" + JSON.stringify(results))
res.send(JSON.stringify(results)) ;
}
Firefox console starts off with this error:
Warning: Prop `autoCapitalize` did not match. Server: "none" Client: "off"
input
form
div
SearchBox#webpack-internal:///../../../../node_modules/react-instantsearch-dom/dist/es/components/SearchBox.js:84:20
Translatable#webpack-internal:///../../../../node_modules/react-instantsearch-core/dist/es/core/translatable.js:45:24
Connector#webpack-internal:///../../../../node_modules/react-instantsearch-core/dist/es/core/createConnector.js:57:24
ConnectorWrapper
InstantSearch#webpack-internal:///../../../../node_modules/react-instantsearch-core/dist/es/widgets/InstantSearch.js:80:20
Search
App#webpack-internal:///./pages/_app.tsx:12:38
PathnameContextProviderAdapter#webpack-internal:///./node_modules/next/dist/shared/lib/router/adapters.js:62:34
ErrorBoundary#webpack-internal:///./node_modules/next/dist/compiled/#next/react-dev-overlay/dist/client.js:301:63
ReactDevOverlay#webpack-internal:///./node_modules/next/dist/compiled/#next/react-dev-overlay/dist/client.js:850:908
Container#webpack-internal:///./node_modules/next/dist/client/index.js:62:1
AppContainer#webpack-internal:///./node_modules/next/dist/client/index.js:172:25
Root#webpack-internal:///./node_modules/next/dist/client/index.js:347:37
See more info here: https://nextjs.org/docs/messages/react-hydration-error
And as I enter characters into the search box I get this:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
I was wondering how I could fix this. Should I use another package rather than InstantSearch, and what would that be? I am inexperienced with javascript.
I checked to see if there was anything wrong with the API connection which it doesn't look like there is. Just between getting the input properly.

Related

unable to fetch api using the redux toolkit

I have watched a tutorial to know how redux toolkit work after watching that he uses some online api providing service now when i am using it i need to customize it for my use so i have did change the name and the urls
here is my store.js
import {configureStore} from '#reduxjs/toolkit'
import {setupListeners} from '#reduxjs/toolkit/query'
import { postApi } from './actions/productAction'
export const store = configureStore({
reducer:{
[postApi.reducerPath]:postApi.reducer
},
// middleware:(getDefaultMiddleware)=>getDefaultMiddleware().concat(postApi.middleware),
// middleware is also created for us, which will allow us to take advantage of caching, invalidation, polling, and the other features of RTK Query.
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(postApi.middleware),
})
setupListeners(store.dispatch)
Here i have the action file which is actually taking the action updating state
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
export const postApi = createApi({
reducerPath:'postApi',//this is unique path which will tell the broweser where it need to store the cookie data
baseQuery: fetchBaseQuery({
baseUrl:'',
}),
endpoints:(builder)=>({
getAllPost: builder.query({
query:()=>({
url:'http://localhost:5000/api/v1/products',
method:'GET'
})
}),
getPostById: builder.query({
query: (id) =>({
url:`posts/${id}`,
method:'GET'
})
})
})
})
export const {useGetAllPostQuery,useGetPostByIdQuery} = postApi
The place where i am calling this function is
import React from 'react'
import {useGetAllPostQuery} from '../../actions/productAction'
// import Card from '../../components/Card';
const HomePage = () => {
console.log(useGetAllPostQuery())
const responseInfo = useGetAllPostQuery()
console.log("the response i am getting is",responseInfo)
return (
<>
<h1>Hello worls</h1>
</>
)
}
export default HomePage
my console where i am getting i dont why this error is coming
the request is rejcted at the same time my post man works on that
The error expanded image
The actual issue is
looks like it is not getting the Url Try adding your base URL like this
export const postApi = createApi({
reducerPath:'postApi',
baseQuery: fetchBaseQuery({
baseUrl:'http://localhost:5000/', //added your base url
}),
endpoints:(builder)=>({
getAllPost: builder.query({
query:()=>({
url:'api/v1/products' // this should take only one argument
})
}),
getPostById: builder.query({
query: (id) =>({
url:`posts/${id}` // this should take only one argument
})
})
})
})
For more Details, you can refer to here rtk query
Bro, I watch that tutorial too, it is old.
Here is the correct way to fetch. Please let me know if this answer help you. And also you auto complete to import. You're not importing from the right place if you follow that tutorial.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import React from 'react'
import {useGetAllPostQuery} from '../../actions/productAction'
// import Card from '../../components/Card';
const HomePage = () => {
console.log(useGetAllPostQuery())
//You can also desctructure it like this {data:responseInfo}, you must not directly change the value.
const {data} = useGetAllPostQuery()
return (
<>
//Then you check if data exist first because the initial value is empty. you can also check like this <div>{data?.map((my data)=> <>...</>))}</div>
<div>{data && data.map((mydata, i)=> (
//You can tag on what you want to display. if you want to display names, then you can say madata.name
<p key={i}>{mydata}</p>
))}</div>
</>
)
}
export default HomePage
To solve cors errors, try this:
Create vercel.json under the root folder.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
{
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
{ "key": "Access-Control-Allow-Origin", "value": "*" }, // Change this to specific domain for better security
{
"key": "Access-Control-Allow-Methods",
"value": "GET,OPTIONS,PATCH,DELETE,POST,PUT"
},
{
"key": "Access-Control-Allow-Headers",
"value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
}
]
}
]
}
Go to /api/index.js file.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
export default async (req, res) => {
const { method } = req;
// This will allow OPTIONS request
if (method === "OPTIONS") {
return res.status(200).send("ok");
}
};
I was not looking console properly the main issue was that the backend was not allowing me to query the api which is kind strange i have not find this using axios means i not need to use cors to access api using axios but if you are doing it using redux tool kit then you need to use cors
npm install cors
then add these line in your main backend file
const cors = require('cors');
const express = require('express');
const app = express();
app.use(cors());

Navigating to dynamically routed page in Next JS returns error

When navigating from a link in the same web app to the dynamically routed page clicking on a link the result is as intended: I navigate to the page for a product (http://localhost/1).
But when I directly navigate by naming the product number specifically in the search bar (navigating to http://localhost/2), I get the following error:
Server Error
TypeError: Cannot read property 'image' of undefined
> | <Image src={"/../public/images/" + p.image}
^
So far I've tried making the types match and reading the Next JS docs on dynamically routing.
I've removed the array zero from the filter but still no resolution.
Could it be possible that the routing only works when clicking on a link in Next JS? Is there some missing setting I've neglected?
pages/[pid].js
import { useRouter } from 'next/router'
import Image from 'next/image'
import data from '../products.json'
export default function Template() {
const router = useRouter()
const { pid } = router.query
const p = data.filter(product => product._id == pid)[0] // Choose one result
return (
<Image src={"/../public/images/" + p.image}
height="500px"
width="500px" />
)
}
products.json
[
{
"_id": 1,
"name": "Toyota",
"image": "toyota.png"
},
{
"_id": 2,
"name": "BMW",
"image": "bmw.png"
}
]
Update: I've tried to hardcode the src attribute in the Image tag and the new error says the other references are the issue. So I can safely say the issue is to do with no object returned when the data object is called.
I solved the issue!
It was not enough to use Dynamic Routes by using the 'useRouter()' function. I also had to define these two functions:
export async function getStaticProps(context) {
// No-op since getStaticPaths needs getStaticProps to be called.
return { props: {} }
}
export async function getStaticPaths() {
const dynamicFiles = products.map(product => (
{
params: { pid: String(product._id) }, // Product IDs are ints in JSON file
}
))
return {
paths: dynamicFiles,
fallback: false
}
}
This makes sense since you wouldn't want random paths to be used as a variable. For example, then a user would be able to specify http://localhost/1234 when 1234 is not a valid option.
https://nextjs.org/learn/basics/dynamic-routes/implement-getstaticprops

How to server side redirect to other page without reloading the page and still keeping the url in Nextjs app?

I have a [slug].js page that will fetch API to get the destination page
export async function getServerSideProps({ query, res }) {
const slug = query.slug;
try {
const destination = await RoutingAPI.matchSlug(slug);
res.writeHead(302, { Location: destination });
res.end();
// return {
// redirect: {
// permanent: true,
// destination,
// },
// }
} catch (error) {
return {
notFound: true
}
}
}
If I client redirect from another page to slug page, it works and keeps URL the same as slug but it makes the browser reload. If I use
return {
redirect: {
permanent: true,
destination,
},
}
it will not reload the browser but it change URL to the destination, not the same as slug. How do i fix this problem? I would appreciate any ideas, thanks
You can use rewrites to achieve this. From the docs:
Rewrites allow you to map an incoming request path to a different destination path.
In your next.config.js add this:
module.exports = {
async rewrites() {
return [
{
source: "/:slug",
destination: "/your-destination",
},
];
},
};
rewrites is an async function that expects an array to be returned holding objects with source and destination properties:
source is the incoming request path pattern.
destination is the path you want to route to.
Ok, I have a solution, it doesn't reload and it doesn't change the url.. however it requires some client side script.. as you see in the example below
Hope you find it to your liking :D
Here's my codesandbox
index.js(from your codesandbox)
import Link from "next/link";
export default function IndexPage() {
return (
<div>
Hello World.{" "}
<Link href="/something-slug">
<a id="myElem">Go to slug pageee</a>
</Link>
<script
dangerouslySetInnerHTML={{
__html: `
let elem=document.getElementById('myElem')
elem.addEventListener('click',async function(event){
event.preventDefault()
console.log(event.path[0].href)
let myFetch=await fetch(event.path[0].href)
let text=await myFetch.text()
let newHTML=document.createElement('html')
newHTML.innerHTML=text
let _next=document.getElementById('__next')
_next.innerHTML=""
let requestedNext=[...newHTML.getElementsByTagName('div')].filter(a=>a.id=="__next")[0]
let scripts=[...requestedNext.getElementsByTagName('script')]
scripts.forEach(a=>eval(a.innerHTML)) //eval any scripts sent by YOUR requested _next
console.log(requestedNext)
_next.innerHTML=requestedNext.innerHTML
})
`
}}
></script>
</div>
);
}
slug.js(again, from your codesandbox)
export default function AboutPage() {
return <div>About us</div>;
}
export async function getServerSideProps({ query, req, res }) {
return {};
}

location is not defined error in react + next js?

I am trying to send some text on basic of hosted url (where my build is deployed).but i am getting this error
ReferenceError: location is not defined
here is my code
https://codesandbox.io/s/laughing-mendel-pf54l?file=/pages/index.js
export const getStaticProps = async ({ preview = false, previewData = {} }) => {
return {
revalidate: 200,
props: {
//req.host
name: location.hostname == "www.google.com" ? "Hello" : "ccccc"
}
};
};
Can you show your imports, because it could be that you are importing router from 'next/client'
Assuming that you are using functional-based component
You need to import router as follows:
import {useRouter} from "next/router";
in your function body:
const router = useRouter();
getStaticProps() is executed at build time in Node.js, which has no location global object – Location is part of the browser API. Additionally, because the code is executed at build time, the URL is not yet known.
Change getStaticProps to getServerSideProps (see documentation). This will mean the function is called at runtime, separately for each request.
From the context object passed to getServerSideProps, pull out the Node.js http.IncomingMessage object.
On this object, look for the Host header.
export const getServerSideProps = async ({ req }) => {
return {
props: {
name: req.headers.host === "www.google.com" ? "Hello" : "ccccc"
}
};
};
Note:
I also changed == to ===, as it's generally advised to use the latter. The former can produce some unexpected results because of silent type conversions.
I also removed revalidate, as this is not applicable to getServerSideProps().

Server Side Render Dynamic Page based on Route Param

I'm starting with Next.js and after going through docs, I cannot figure out how to get the route param code inside getStaticPaths method as shown below!?. code is not known before hand by any means and it can be anything.
I don't want to call api and get the data using useEffect inside the component.
File: pages/post/[code].js
import React from 'react';
import apiCall from 'api/something';
export default ({post}) => {
return <>
render components here based on prop `post`
</>
}
export async function getStaticPaths() {
// How to get [code] from the route here, which can be used below?
return {
paths: // NEED [code] HERE from current route,
fallback: false
}
}
export async function getStaticProps(ctx) {
return {
props: {
// [ctx.code] resolved from current route with the help of getStaticPaths,
post: apiCall(ctx.code)
}
}
}
I've tried getServerSideProps which works for me:
export const getServerSideProps = async (ctx) => {
return {
props: {
post: await apiCall(ctx.query.code)
}
};
};
But it fails when I do next export stating:
pages with getServerSideProps can not be exported. See more info here: https://err.sh/next.js/gssp-export
After investigating further on this error I found this solution, which is not feasible for me as my app is hosted on Heroku.
I'm trying to server-side render the html along with the data based on the route param code. But not able to do so now.
The purpose of the function getStaticPaths is to generate a list of paths for which static HTML will be rendered at build time. For example, for a list of 10 posts, you can generate 10 posts/[id] routes ahead of time if you know the id of the posts.
How getStaticPaths works with dynamic routes in more details..
Suppose you have a dynamic route /posts/[postId] if you choose to use static-generation you have to generate a list of paths that will include the postId as a route param and for each path returned, the function getStaticProps will be called to query the data at build time. Example,
// for /post/[postId]
export const getStaticPaths = async () => {
// if you know all the postId ahead of time
const paths = [
{ params: { postId: '1234' } }, // keep in mind postId has to be a string
{ params: { postId: '3792' } },
{ params: { postId: '1749' } },
]
return {
paths,
fallback: false // we are disabling fallback because we know all the paths ahead of time
}
}
// for each path returned getStaticProps will be called at build time
export const getStaticProps = async (context) => {
// you have access to the postId params that you returns from
// getStaticPaths here
const postId = context.params.postId
// now you can query the data from postId and return as props
return {
props: // queried data
}
}
If fallback is set to false any for any route path that is not returned from the function getStaticPaths nextjs will simply show a 404 error page.
How to use fallback: true to generate static pages for route params not known ahead of time
If you know some postId of the posts and the data for the posts do not change very often, you can choose to generate the pages with fallback property set to true, which will display a fallback version of the page for the paths that are not returned from the function getStaticPaths. And on request for the page nextjs will call getStaticProps and send the data as JSON which will be used to render the page in the browser.
Example,
// for /post/[postId]
export const getStaticPaths = async () => {
// you can get how many ever postIds are know ahead of time
// and return as paths with fallback set to true
const posts = // queried data from db or fetched from remote API
const paths = posts.map(post => { params:{ postId: post.id.toString() }})
return {
paths,
fallback: true
}
}
// in your page Component check for fallback and render a loading indicator
import { useRouter } from 'next/router';
const MyPage = (props) => {
// before you do anything
const router = useRouter();
if (router.isFallback) {
return <div>Loading....</div>
}
// rest of your page logic
}
If your data is very dynamic, let's say changing every 30mins or an hour or so. You can choose to use server-side rendering which will fetch the data on per request basis, but TTFB(time to first byte) will be higher. For example,
// for /post/[postId]
export const getServerSideProps = async (context) => {
// you also have access to the param postId from the context
const postId = context.params.postId
// query the data based on the postId and return as props
return {
props: // queried data
}
}
Keep in mind if you choose to go with getServerSideProps the function will be called on per-request basis so time to first byte will be higher.
Depending on use-cases you can also use static generation with client-side data fetching using swr from nextjs team repo link.
As I understand, you want to statically generate dynamic routes at build time.
To do so you need to let Next.js know what pages to generate, by specifying all codes.
export async function getStaticPaths() {
// you don't need here a code from current route
// but you need to specify all known post codes
return {
paths: [
{ params: { code: '1' } },
{ params: { code: '2' } },
{ params: { code: '3' } },
]
fallback: false
}
}
You would need to re-build app every time you change the posts.
Use getServerSideProps if you don't want to re-build project every time. Then the data would be fetched at request time. You can't export it because it requires Node.js server.

Categories