Getting TRPCClientError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON when trying to call a back end api with Trcp - vite

I am planning on building an app using SST and tRPC. I have never used either so I am going through the docs and quick start to better understand the material. I came across an issue where the call is not rendering on the front end. Im not sure if I have the router wrong or something else in the backend. Everytime I make a request it will give this error TRPCClientError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. But im not sure where its coming from.
Stacks
import { StackContext, Api, ViteStaticSite } from "#serverless-stack/resources";
export function MyStack({ stack }: StackContext) {
const api = new Api(stack, "api", {
routes: {
"GET /todo": "functions/todo.handler"
},
cors : true,
});
const site = new ViteStaticSite(stack, "site", {
path: "frontend",
buildCommand: "npm run build",
environment: {
REACT_APP_API_URL: api.url,
},
});
stack.addOutputs({
SITE: site.url,
});
}
router
import { initTRPC } from '#trpc/server';
import { z } from 'zod';
export const t = initTRPC.create();
const appRouter = t.router({
greeting: t.procedure
.input(
z.object({
name: z.string(),
})
)
.query(({input}) => {
return {
text: `Hello ${input?.name ?? 'world'}`
};
}),
});
export type AppRouter = typeof appRouter;
import { awsLambdaRequestHandler } from '#trpc/server/adapters/aws-lambda';
export const handler = awsLambdaRequestHandler({
router: appRouter
})
frontend
import React, { useState } from 'react';
import ReactDOM from 'react-dom/client'
import './index.css'
import { QueryClient, QueryClientProvider } from '#tanstack/react-query';
import { httpBatchLink } from '#trpc/client';
import { trpc } from './trpc';
const apiUrl = import.meta.env.REACT_APP_API_URL;
function App() {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: `${apiUrl}/todo`
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<Sample />
</QueryClientProvider>
</trpc.Provider>
);
}
function Sample(){
const result = trpc.greeting.useQuery({name: 'will'})
return (
<div>
<div>{result.isLoading ? "Loading..." : result.data?.text}</div>
</div>
)
}

Related

Im trying to implement authentication in next.js using next-auth and next.js middleware, but im getting an error when using it in every route

Im trying to implement auth logic using this example but I am trying to implement it in every route like so
export const config = {
matcher: ['/:path*'],
};
but my browser breaks because too many requests, but when I change the matcher array to specific routes, it works, like this:
matcher: ['/teacher','/student'],
with the matcher like this, auth works for the pages (student,teacher) but not other pages.
middleware.ts:
import { NextRequest, NextResponse } from 'next/server';
import { withAuth } from 'next-auth/middleware';
const PUBLIC_FILE = /\.(.*)$/;
export default withAuth(
async function middleware(req: NextRequest) {
if (
req.nextUrl.pathname.startsWith('/_next') ||
req.nextUrl.pathname.includes('/api/') ||
PUBLIC_FILE.test(req.nextUrl.pathname)
) {
return;
}
if (req.nextUrl.locale === 'default') {
const locale = req.cookies.get('NEXT_LOCALE') || 'en';
return NextResponse.rewrite(
new URL(
`/${locale}${req.nextUrl.pathname}${req.nextUrl.search}`,
req.url,
),
);
}
},
{
callbacks: {
authorized({ req, token }) {
return !!token;
},
},
},
);
export const config = {
matcher: ['/:path*', '/teacher'],
};

Generate one single file for each entry with Vite

Basically what I need is generate one single file that contain all vendors and dependencies for each entry..
export default defineConfig({
plugins: [ react() ],
build: {
rollupOptions: {
input: {
popup: path.resolve(pagesDirectory, 'popup', 'index.html'),
background: path.resolve(pagesDirectory, 'background', 'index.ts'),
content_script: path.resolve(pagesDirectory, 'content_script', 'ContentScript.tsx')
},
output: {
entryFileNames: 'src/pages/[name]/index.js',
chunkFileNames: isDevelopment ? 'assets/js/[name].js' : 'assets/js/[name].[hash].js',
assetFileNames: (assetInfo) => {
const { dir, name: _name } = path.parse(assetInfo.name);
const assetFolder = getLastElement(dir.split('/'));
const name = assetFolder + firstUpperCase(_name);
return `assets/[ext]/${name}.chunk.[ext]`;
}
}
}
}})
In this case.. will be one file for popup, background and content_script
Here is one example of ContentScript.tsx file...
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Badge from './badge';
function init(query: string) {
const appContainer = document.querySelector('#search') as HTMLElement;
if (!appContainer) {
throw new Error('Can not find AppContainer');
}
const rootElement = document.createElement('div');
rootElement.setAttribute('id', 'web-answer-content-script');
appContainer.insertBefore(rootElement, appContainer.firstChild);
const root = createRoot(rootElement, {});
root.render(
<React.StrictMode>
<Badge query={query} />
</React.StrictMode>
);
}
const searchParameters = new URLSearchParams(window.location.search);
if (searchParameters.has('q')) {
const query = searchParameters.get('q');
init(query);
}
import MessageSender = chrome.runtime.MessageSender;
function handleMessageReceived(message: string, sender: MessageSender) {
console.log('>>> MESSAGE RECEIVED', message, sender);
}
chrome.runtime.onMessage.addListener(handleMessageReceived);
With this configuration I'm getting this..
and my content_scripts/index.js ..
import { j as n, c as a, r as c } from '../../../assets/jsx-dev-runtime.a077470a.js';
// ..rest of the code...
As you can see.. I don't want this import... statement ..

RTK Query in Redux-Toolkit is returning data of undefined, when I console.log the data it appears in console

I was trying to display an array of data fetched from my custom server with RTK Query using Next.js (React framework). And this is my first time using RTK Query. Whenever I console.log the data, it appears in the browser console. But whenever I try to map the data to render it in the browser, it keeps throwing an error saying Cannot read properties of undefined (reading 'map').
I figured Next.js always throws an error if an initial state is undefined or null even if the state change. This link talked about solving the problem using useMemo hook https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics
But I didn't understand it well. Please kindly help me out with displaying the data.
Here is the BaseQuery function example I followed, it was derived from redux toolkit docmentation https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#axios-basequery
import axios from "axios";
const axiosBaseQuery =
({ baseUrl } = { baseUrl: "" }) =>
async ({ url, method, data }) => {
try {
const result = await axios({ url: baseUrl + url, method, data });
return { data: result.data };
} catch (axiosError) {
let err = axiosError;
return {
error: { status: err.response?.status, data: err.response?.data },
};
}
};
export default axiosBaseQuery;
I make the GET request here
import { createApi } from "#reduxjs/toolkit/query/react";
import axiosBaseQuery from "./axiosBaseQuery";
export const getAllCarsApi = createApi({
reducerPath: "getAllCarsApi",
baseQuery: axiosBaseQuery({
baseUrl: "http://localhost:5000/",
}),
endpoints(build) {
return {
getAllCars: build.query({
query: () => ({ url: "all-cars", method: "get" }),
}),
};
},
});
export const { useGetAllCarsQuery } = getAllCarsApi;
This is my redux store
import { configureStore } from "#reduxjs/toolkit";
import { getAllCarsApi } from "./getAllCarsApi";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
const store = configureStore({
reducer: { [getAllCarsApi.reducerPath]: getAllCarsApi.reducer },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(getAllCarsApi.middleware),
});
setupListeners(store.dispatch);
export default store;
I provide the store to the _app.js file.
import "../styles/globals.css";
import axios from "axios";
import { MyContextProvider } from "#/store/MyContext";
import { Provider } from "react-redux";
import store from "#/store/ReduxStore/index";
axios.defaults.withCredentials = true;
function MyApp({ Component, pageProps }) {
return (
<MyContextProvider>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</MyContextProvider>
);
}
export default MyApp;
I get the data here in my frontend.
import { useGetAllCarsQuery } from "#/store/ReduxStore/getAllCarsApi";
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};
export default theTest;
This is a timing thing.
Your component will always render immediately and it will not defer rendering until data is there. That means it will also render before your data has been fetched. So while the data is still loading, data is undefined - and you try to map over that.
You could do things like just checking if data is there to deal with that:
const theTest = () => {
const { data, isLoading, error } = useGetAllCarsQuery();
return (
<div>
{data && data.map((theData, i) => (
<h1 key={i}>{theData}</h1>
))}
<h1>Hello</h1>
</div>
);
};

next-i18next Jest Testing with useTranslation

Testing libs...always fun. I am using next-i18next within my NextJS project. We are using the useTranslation hook with namespaces.
When I run my test there is a warning:
console.warn
react-i18next:: You will need to pass in an i18next instance by using initReactI18next
> 33 | const { t } = useTranslation(['common', 'account']);
| ^
I have tried the setup from the react-i18next test examples without success. I have tried this suggestion too.
as well as just trying to mock useTranslation without success.
Is there a more straightforward solution to avoid this warning? The test passes FWIW...
test('feature displays error', async () => {
const { findByTestId, findByRole } = render(
<I18nextProvider i18n={i18n}>
<InviteCollectEmails onSubmit={jest.fn()} />
</I18nextProvider>,
{
query: {
orgId: 666,
},
}
);
const submitBtn = await findByRole('button', {
name: 'account:organization.invite.copyLink',
});
fireEvent.click(submitBtn);
await findByTestId('loader');
const alert = await findByRole('alert');
within(alert).getByText('failed attempt');
});
Last, is there a way to have the translated plain text be the outcome, instead of the namespaced: account:account:organization.invite.copyLink?
Use the following snippet before the describe block OR in beforeEach() to mock the needful.
jest.mock("react-i18next", () => ({
useTranslation: () => ({ t: key => key }),
}));
Hope this helps. Peace.
use this for replace render function.
import { render, screen } from '#testing-library/react'
import DarkModeToggleBtn from '../../components/layout/DarkModeToggleBtn'
import { appWithTranslation } from 'next-i18next'
import { NextRouter } from 'next/router'
jest.mock('react-i18next', () => ({
I18nextProvider: jest.fn(),
__esmodule: true,
}))
const createProps = (locale = 'en', router: Partial<NextRouter> = {}) => ({
pageProps: {
_nextI18Next: {
initialLocale: locale,
userConfig: {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
},
},
} as any,
router: {
locale: locale,
route: '/',
...router,
},
} as any)
const Component = appWithTranslation(() => <DarkModeToggleBtn />)
const defaultRenderProps = createProps()
const renderComponent = (props = defaultRenderProps) => render(
<Component {...props} />
)
describe('', () => {
it('', () => {
renderComponent()
expect(screen.getByRole("button")).toHaveTextContent("")
})
})
I used a little bit more sophisticated approach than mocking to ensure all the functions work the same both in testing and production environment.
First, I create a testing environment:
// testing/env.ts
import i18next, { i18n } from "i18next";
import JSDomEnvironment from "jest-environment-jsdom";
import { initReactI18next } from "react-i18next";
declare global {
var i18nInstance: i18n;
}
export default class extends JSDomEnvironment {
async setup() {
await super.setup();
/* The important part start */
const i18nInstance = i18next.createInstance();
await i18nInstance.use(initReactI18next).init({
lng: "cimode",
resources: {},
});
this.global.i18nInstance = i18nInstance;
/* The important part end */
}
}
I add this environment in jest.config.ts:
// jest.config.ts
export default {
// ...
testEnvironment: "testing/env.ts",
};
Sample component:
// component.tsx
import { useTranslation } from "next-i18next";
export const Component = () => {
const { t } = useTranslation();
return <div>{t('foo')}</div>
}
And later on I use it in tests:
// component.test.tsx
import { setI18n } from "react-i18next";
import { create, act, ReactTestRenderer } from "react-test-renderer";
import { Component } from "./component";
it("renders Component", () => {
/* The important part start */
setI18n(global.i18nInstance);
/* The important part end */
let root: ReactTestRenderer;
act(() => {
root = create(<Component />);
});
expect(root.toJSON()).toMatchSnapshot();
});
I figured out how to make the tests work with an instance of i18next using the renderHook function and the useTranslation hook from react-i18next based on the previous answers and some research.
This is the Home component I wanted to test:
import { useTranslation } from 'next-i18next';
const Home = () => {
const { t } = useTranslation("");
return (
<main>
<div>
<h1> {t("welcome", {ns: 'home'})}</h1>
</div>
</main>
)
};
export default Home;
First, we need to create a setup file for jest so we can start an i18n instance and import the translations to the configuration. test/setup.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import homeES from '#/public/locales/es/home.json';
import homeEN from '#/public/locales/en/home.json';
i18n.use(initReactI18next).init({
lng: "es",
resources: {
en: {
home: homeEN,
},
es: {
home: homeES,
}
},
fallbackLng: "es",
debug: false,
});
export default i18n;
Then we add the setup file to our jest.config.js:
setupFilesAfterEnv: ["<rootDir>/test/setup.ts"]
Now we can try our tests using the I18nextProvider and the useTranslation hook:
import '#testing-library/jest-dom/extend-expect';
import { cleanup, render, renderHook } from '#testing-library/react';
import { act } from 'react-dom/test-utils';
import { I18nextProvider, useTranslation } from 'react-i18next';
import Home from '.';
describe("Index page", (): void => {
afterEach(cleanup);
it("should render properly in Spanish", (): void => {
const t = renderHook(() => useTranslation());
const component = render(
<I18nextProvider i18n={t.result.current.i18n}>
<Home / >
</I18nextProvider>
);
expect(component.getByText("Bienvenido a Pocky")).toBeInTheDocument();
});
it("should render properly in English", (): void => {
const t = renderHook(() => useTranslation());
act(() => {
t.result.current.i18n.changeLanguage("en");
});
const component = render(
<I18nextProvider i18n={t.result.current.i18n}>
<Home/>
</I18nextProvider>
);
expect(component.getByText("Welcome to Pocky")).toBeInTheDocument();
});
});
Here we used the I18nextProvider and send the i18n instance using the useTranslation hook. after that the translations were loaded without problems in the Home component.
We can also change the selected language running the changeLanguage() function and test the other translations.

Graphql Bad Request on page load

Hello Everyone and Happy Holidays,
I'm building a website with KeystoneJS and NextJS. I have added Apollo Client in between.
However, I'm having an issue with Apollo Client now. I have tried different places to put in as well but the result was the same, Anyway here is my _app.tsx file
import { useReducer } from "react";
import { useRouter } from "next/router";
import { ThemeProvider } from "styled-components";
import { HttpLink } from "apollo-link-http";
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { Query, KeystoneProvider } from "#keystonejs/apollo-helpers";
import { ApolloProvider, gql } from "#apollo/client";
import { primaryTheme } from "../styles/theme";
import GlobalStyle from "../styles/global";
import { initialState, globalReducer } from "../context/reducer";
import Meta from "../components/global/Meta";
import { Header } from "../components/global/Header";
import { globalContext } from "../context/contex";
import Footer from "../components/global/Footer";
import Loading from "../components/global/Loading";
const client = new ApolloClient({
ssrMode: true,
cache: new InMemoryCache(),
link: new HttpLink({
uri: process.env.NEXT_PUBLIC_API_URL,
fetchOptions: {
mode: 'cors'
},
}),
});
const QUERY = gql`
query {
allOperations{
id
name
}
}
`
const MyApp = ({ Component, pageProps }) => {
const [store, dispatch] = useReducer(globalReducer, initialState);
const router = useRouter();
return (
<ApolloProvider client={client}>
<KeystoneProvider>
<ThemeProvider theme={primaryTheme}>
<Meta />
{router.route !== "/" && <Header />}
<Query
query={QUERY}
>
{({ loading, data }) => {
console.log(data);
if(loading){
return <Loading />
} else {
return <Component {...pageProps} />
}}}
</Query>
<Footer />
<GlobalStyle />
</ThemeProvider>
</KeystoneProvider>
</ApolloProvider>
);
};
export default MyApp;
On page load on every page, I get this 400 Bad request Error says
{errors: [{,…}]}
errors: [{,…}]
0: {,…}
extensions: {code: "GRAPHQL_VALIDATION_FAILED", exception: {stacktrace: [,…]}}
code: "GRAPHQL_VALIDATION_FAILED"
exception: {stacktrace: [,…]}
stacktrace: [,…]
0: "GraphQLError: Field "queries" of type "_ListQueries" must have a selection of subfields. Did you mean "queries { ... }"?"
1: " at Object.Field (/Users/artticfox/Desktop/Work/frank/backend/node_modules/graphql/validation/rules/ScalarLeafsRule.js:40:31)"
2: " at Object.enter (/Users/artticfox/Desktop/Work/frank/backend/node_modules/graphql/language/visitor.js:323:29)"
3: " at Object.enter (/Users/artticfox/Desktop/Work/frank/backend/node_modules/graphql/utilities/TypeInfo.js:370:25)"
4: " at visit (/Users/artticfox/Desktop/Work/frank/backend/node_modules/graphql/language/visitor.js:243:26)"
5: " at Object.validate (/Users/artticfox/Desktop/Work/frank/backend/node_modules/graphql/validation/validate.js:69:24)"
6: " at validate (/Users/artticfox/Desktop/Work/frank/backend/node_modules/apollo-server-core/dist/requestPipeline.js:221:34)"
7: " at Object.<anonymous> (/Users/artticfox/Desktop/Work/frank/backend/node_modules/apollo-server-core/dist/requestPipeline.js:118:42)"
8: " at Generator.next (<anonymous>)"
9: " at fulfilled (/Users/artticfox/Desktop/Work/frank/backend/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)"
10: " at runMicrotasks (<anonymous>)"
11: " at processTicksAndRejections (internal/process/task_queues.js:93:5)"
locations: [{line: 5, column: 7}]
0: {line: 5, column: 7}
column: 7
line: 5
message: "Field "queries" of type "_ListQueries" must have a selection of subfields. Did you mean "queries { ... }"?"
name: "ValidationError"
uid: "ckjbkc1j9001qng0d2itof7d9"
but I don't request list queries at all.
The first 2 API calls are 204 last one is 200 and I get the query fine. My assumption is this is happening because of SSR but I need a solution. I tried to pass by with loading and stuff as well but it didn't work.
And here is my KeystoneJS setup.
const { Keystone } = require("#keystonejs/keystone");
const { GraphQLApp } = require("#keystonejs/app-graphql");
const { AdminUIApp } = require("#keystonejs/app-admin-ui");
const { MongooseAdapter: Adapter } = require("#keystonejs/adapter-mongoose");
const { PasswordAuthStrategy } = require("#keystonejs/auth-password");
const { NextApp } = require('#keystonejs/app-next');
require("dotenv").config();
const OperationSchema = require("./lists/Operation.ts");
const UserSchema = require("./lists/User.ts");
const PROJECT_NAME = "frank";
const adapterConfig = {
mongoUri: process.env.DATABASE,
};
/**
* You've got a new KeystoneJS Project! Things you might want to do next:
* - Add adapter config options (See: https://keystonejs.com/keystonejs/adapter-mongoose/)
* - Select configure access control and authentication (See: https://keystonejs.com/api/access-control)
*/
const keystone = new Keystone({
adapter: new Adapter(adapterConfig),
});
keystone.createList("Operation", OperationSchema);
keystone.createList("User", UserSchema);
const authStrategy = keystone.createAuthStrategy({
type: PasswordAuthStrategy,
list: "User",
config: {
identityField: "username",
secretField: "password",
},
});
module.exports = {
keystone,
apps: [
new GraphQLApp(),
new AdminUIApp({ name: PROJECT_NAME, enableDefaultRoute: false }),
new NextApp({ dir: '../frontend/' }),
],
};
Backend is running on localhost:3000
The frontend is running on localhost:7777
Thanks in advance.
Happy Holidays
I was having the same issue and have figured out a workaround for this.
The error is being caused by an outdated GraphQL query in the #keystonejs/apollo-helpers. You can go through an update the code in the dist directory and update all files that include the META_QUERY variable to include the missing query subfields.
query ListMeta {
_ksListsMeta {
schema {
type
queries {
item
list
meta
__typename
}
relatedFields {
type
fields
}
}
}
}
Please keep in mind this workaround will not work when the #keystonejs/apollo-helpers package is reinstalled / updated.
Found the reason for this error, Keystoneprovider was creating this issue for some reason. If anybody knows the reason, it would be nice to know the reason.

Resources