Hi Everyone _Why does my `TypesenseInstantSearchA...
# community-help
s
Hi Everyone Why does my
TypesenseInstantSearchAdapter
setup in React Native fail to connect, even though raw Axios calls to the same Railway-hosted Typesense endpoint succeed? I'm passing the correct hostname and API key, using port 8080 and protocol 'https'. Could it be an adapter limitation in mobile environments, or is there a mismatch in how the host or connection settings are parsed?
import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter';
import config from '@/utils/config'; const createTypesenseAdapter = (lat: number, lng: number, radius = '30km') => { console.log("lat is" + lat + " lng is" + lng + " radius is" + radius); console.log("config is" + JSON.stringify(config, null, 2)); return new TypesenseInstantSearchAdapter({ server: { apiKey: apikey, nodes: [ { host: 'typesense-production-uytfg.up.railway.app', port: 8080, protocol: 'https' } ], timeoutSeconds: 50 } , additionalSearchParameters: { query_by: [ 'name_en', 'name_am', 'description_en', 'description_am', 'address', 'services.name_en', 'services.name_am', 'services.description_en', 'services.description_am', ], // Dynamically add location filter filter_by: lat && lng ?
location:([${lat}, ${lng}], radius: ${radius})
: '', sort_by: lat && lng ?
location(${lat}, ${lng}):asc
: 'name_en:asc', }, }) }; export default createTypesenseAdapter;
k
cc @Fanis Tharropoulos
f
Which version of react native are you using?
s
i am using version 0.79.5
f
https://github.com/axios/axios/issues/6957 There's actually an issue with axios on React Native. Is this on an emulator or on a real device?
s
it's the same with both
f
Can you share the request and the response? If it's a generic network error, it should match the issue at axios
s
const axios = require('axios');
let config = { method: 'get', maxBodyLength: Infinity, url: 'https://typesense-production-dfdf.up.railway.app/collections/churches/documents/search?q=geb&query_by=name_en', headers: { 'X-TYPESENSE-API-KEY': 'apikey' } }; axios.request(config) .then((response) => { console.log(JSON.stringify(response.data)); }) .catch((error) => { console.log(error); });
and here is the response
{
"facet_counts": [], "found": 0, "hits": [], "out_of": 0, "page": 1, "request_params": { "collection_name": "churches", "first_q": "geb", "per_page": 10, "q": "geb" }, "search_cutoff": false, "search_time_ms": 0 }
f
Are there any equivalent requests being sent from Typesense as well? Also, what's the version of Axios you're using? You can see it using
npm list axios
s
"axios": "^1.11.0", "typesense-instantsearch-adapter": "^2.9.0", "react-instantsearch-core": "^7.16.1",
i am using react-instant-search-core to leverage the search ui components
f
Caret-based package dependencies are valid for minor patch versions in semver (2.9.0). If you use the
list
command it will show you the exact version that you have installed.
s
this is the result i get from npm list axios ├── axios@1.11.0 └─┬ typesense-instantsearch-adapter@2.9.0 └─┬ typesense@2.1.0-10 └── axios@1.11.0 deduped
f
It's the same dependency... Can you share the request sent by the adapter?
s
This is How i call the adapter
import { Dimensions, TouchableOpacity, View, StyleSheet, ScrollView } from 'react-native';
import { InstantSearch, useHits } from 'react-instantsearch-core';
import typesenseInstantsearchAdapter from '@/lib/typesense.adapter';
import SearchBox from '../box';
import { useAppTheme } from '@/utils/useAppTheme';
import { useState } from 'react';
import { useFontScaling } from '@/hooks/useFontScaling';
import { useRouter } from 'expo-router';
import { TapGestureHandler } from 'react-native-gesture-handler';
import Image from '@/components/elements/Image';
import { Text } from '@/components/elements/Text';
import { fonts, ThemedStyle } from '@/theme';
import { getFontSize } from '@/utils/font';
import SearchResult from '../results';
import { SafeAreaView } from 'react-native-safe-area-context';
import theme from '@/providers/theme';
import createTypesenseAdapter from '@/lib/typesense.adapter';
import { DataPersistKeys, useDataPersist } from '@/hooks/useDataPersist';
const screenWidth = Dimensions.get('window').width;
// Define TypeScript interface for church data
interface Church {
id: number;
title: string;
images: any[];
}
const SearchResultItem: React.FC = ({ hit, index }) => {
const { themed, theme } = useAppTheme();
const styles = themed(themedlayoutStyles);
const [currentPage, setCurrentPage] = useState<number>(0);
const fontProps = useFontScaling();
const router = useRouter();
const distanceInKm = (hit.geo_distance_meters.location / 1000).toFixed(2);
const handlePress = () => {
router.push('/church/profile');
};
return (
<View style={[
index % 2 ? { paddingLeft: 10 } : { paddingRight: 10 }, { flex: 1 },
styles.churchItemContainer,
]}>
{/* TapGestureHandler around PagerView */}
<TapGestureHandler onActivated={handlePress}>
<View style={styles.churchCard}>
<View style={styles.imageWrapper}>
<View style={styles.imagePager}>
<Image style={styles.churchImage} source={require('@/assets/images/nearby-church.png')} contentFit="cover" />
</View>
</View>
</View>
</TapGestureHandler>
{/* Text section */}
<TouchableOpacity onPress={handlePress} activeOpacity={0.8}>
<View style={styles.churchInfoContainer}>
<View style={styles.churchTitleContainer}>
<Text
{...fontProps}
style={[styles.churchTitle, { flex: 1 }]}
numberOfLines={1}
>
{hit.name_en}
</Text>
</View>
<Text
{...fontProps}
style={styles.churchDistance}
numberOfLines={1}
>
{distanceInKm} km away
</Text>
</View>
</TouchableOpacity>
</View>
);
};
export default function layout() {
const { theme, themed } = useAppTheme();
const styles = themed(themedlayoutStyles);
const { getPersistData, setPersistData, removeAllPersistData } = useDataPersist();
const [searchClient, setSearchClient] = useState<any>(null);
// Fetch location and set search client
useState(() => {
(async () => {
const storedLocation = await getPersistData<{ latitude: number, longitude: number }>(DataPersistKeys.USER_LOCATION);
const latitude = storedLocation?.latitude ?? 0;
const longitude = storedLocation?.longitude ?? 0;
setSearchClient(createTypesenseAdapter(latitude, longitude).searchClient);
})();
});
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView
style={styles.root}
contentContainerStyle={{ paddingBottom: theme.spacing.xxl }}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
{searchClient && (
<InstantSearch indexName="churches" searchClient={searchClient}>
<SearchBox />
<SearchResult hitComponent={SearchResultItem} />
</InstantSearch>
)}
</ScrollView>
</SafeAreaView>
);
}
const themedlayoutStyles: ThemedStyle<any> = theme =>
StyleSheet.create({
root: {
flex: 1,
backgroundColor: theme.colors.background,
},
churchItemContainer: {
marginBottom: theme.spacing.md,
paddingHorizontal: theme.spacing.md,
width: '100%',
},
churchCard: {
height: 90,
width: '100%',
position: 'relative',
overflow: 'hidden',
backgroundColor: theme.colors.componentBackground,
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
},
imageWrapper: {
flex: 1,
overflow: 'hidden',
},
imagePager: {
flex: 1,
},
churchImage: {
width: '100%',
height: '100%',
},
churchInfoContainer: {
paddingTop: theme.spacing.xs,
backgroundColor: theme.colors.componentBackground,
borderBottomLeftRadius: 15,
borderBottomRightRadius: 15,
paddingLeft: 15
},
churchTitleContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
churchTitle: {
fontFamily: fonts.roboto.regular,
color: theme.colors.text.default,
fontSize: getFontSize(14),
flex: 1,
},
churchDistance: {
fontFamily: fonts.roboto.bold,
color: theme.colors.text.default,
fontSize: getFontSize(12),
paddingBottom: theme.spacing.xs,
},
});
//SearchBox
import { TextInput, View, StyleSheet } from 'react-native';
import { useFocusEffect, } from 'expo-router'; import { useAppTheme } from '@/utils/useAppTheme'; import { fonts, ThemedStyle } from '@/theme'; import { getFontSize } from '@/utils/font'; import FilterIcon from '@/components/icons/explore/Filter'; import { useCallback, useRef, useState } from 'react'; import { useSearchBox } from 'react-instantsearch-core'; export default function SearchBox(props) { const { themed } = useAppTheme(); const styles = themed(themedHomeStyles); const inputRef = useRef<TextInput>(null); // Ensure focus on every screen visit const focusInput = useCallback(() => { setTimeout(() => { if (inputRef.current) { inputRef.current.focus(); } }, 100); // Small delay to ensure input is ready }, []); useFocusEffect( useCallback(() => { focusInput(); }, [focusInput]), ); const { query, refine } = useSearchBox(props); const [inputValue, setInputValue] = useState(query); function setQuery(newQuery) { setInputValue(newQuery); refine(newQuery); } // Track when the InstantSearch query changes to synchronize it with // the React state. // We bypass the state update if the input is focused to avoid concurrent // updates when typing. if (query !== inputValue && !inputRef.current?.isFocused()) { setInputValue(query); } return ( <View style={styles.searchContainer}> <View style={styles.searchBox}> <TextInput ref={inputRef} style={styles.searchInput} placeholder="Search for places, events, people" placeholderTextColor="rgba(0, 0, 0, 0.6)" autoFocus={true} value={inputValue} // control the input onChangeText={setQuery} // update Algolia query /> <FilterIcon height={24} width={24} /> </View> </View> ); } const themedHomeStyles: ThemedStyle<any> = theme => StyleSheet.create({ root: { flex: 1, backgroundColor: theme.colors.background, }, searchContainer: { height: theme.spacing.searchBox, marginTop: theme.spacing.lg, paddingHorizontal: theme.spacing.md, }, searchBox: { flex: 1, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: theme.spacing.lg, backgroundColor: theme.colors.componentBackground, borderRadius: 50, height: 46, }, searchInput: { flex: 1, fontFamily: fonts.roboto.regular, fontSize: getFontSize(14), color: theme.colors.text.default, }, });
f
Isn't there an equivalent of the browser dev tools in android studio so you can check actual API request?
s
This is the log that i get from expo dev console
f
What about the network tab?
s
it doesn't show anything i think there network inspection tool is in unstable version so it is difficult to get the actual network request and response. what about does the typesense adapter have network logging options?
f
It relies on the typesense-js client library. Does using the client directly work?
s
did you mean without the adapter?
f
Yup, just instantiating the client object
s
i have tested it and it's the same
f
Will try to replicate it in android studio