useSession returning session object as null in production nextauth - next

I am using next auth to provide google sign up for my project and it was working fine in development but when I deployed it the session object is returning null. The env file contains all the necessary values nextauth_url,nextauth_secret,google_id, and google_secret and the redirect Uri is also set in the google console.
[...nextauth].ts
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code"
}
}
}),
],
callbacks: {
async session({ session, token, user }) {
console.log(user.id)
// Send properties to the client, like an access_token from a provider.
session.user.userId = user.id;
console.log(session.user.userId)
return session
}
},
secret:process.env.NEXT_PUBLIC_SECRET,
debug: true,
})
index.tsx
import type { NextPage } from 'next'
import { useSession} from 'next-auth/react';
import {useEffect} from 'react';
import {useSelector, useDispatch } from 'react-redux';
import { RootState } from '../app/store';
import {addCurrentUser} from '../components/currentUserSlice'
import SignUpLoginFullScreen from '../components/SignUpLoginFullScreen';
import LoadingScreen from '../components/LoadingScreen';
import MainSection from '../components/MainSection';
import PostsList from '../components/Posts/PostsList';
const Home: NextPage = () => {
const {data:session,status}=useSession();
console.log(session,status)
const currentUser=useSelector((state:RootState)=>state.currentUser);
console.log(currentUser)
const dispatch=useDispatch();
useEffect(()=>{
if(session?.user){
console.log(session.user)
dispatch(
addCurrentUser({name:session.user.name,
email:session.user.email,
image:session.user.image,
userId:session?.user.userId}))
}
},[session?.user,dispatch,session?.user.userId])
return (
<>
{!session && status==='loading' &&
<LoadingScreen/>
}
{session &&
<MainSection>
<PostsList currUserId={currentUser.userId} errMsg='No posts found'/>
</MainSection>
}
{!session &&
<SignUpLoginFullScreen/>
}
</>
)
}
export default Home

Related

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

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>
)
}

Fastify CLI decorators undefined

I'm using fastify-cli for building my server application.
For testing I want to generate some test JWTs. Therefore I want to use the sign method of the fastify-jwt plugin.
If I run the application with fastify start -l info ./src/app.js everything works as expected and I can access the decorators.
But in the testing setup I get an error that the jwt decorator is undefined. It seems that the decorators are not exposed and I just can't find any error. For the tests I use node-tap with this command: tap \"test/**/*.test.js\" --reporter=list
app.js
import { dirname, join } from 'path'
import autoload from '#fastify/autoload'
import { fileURLToPath } from 'url'
import jwt from '#fastify/jwt'
export const options = {
ignoreTrailingSlash: true,
logger: true
}
export default async (fastify, opts) => {
await fastify.register(jwt, {
secret: process.env.JWT_SECRET
})
// autoload plugins and routes
await fastify.register(autoload, {
dir: join(dirname(fileURLToPath(import.meta.url)), 'plugins'),
options: Object.assign({}, opts),
forceESM: true,
})
await fastify.register(autoload, {
dir: join(dirname(fileURLToPath(import.meta.url)), 'routes'),
options: Object.assign({}, opts),
forceESM: true
})
}
helper.js
import { fileURLToPath } from 'url'
import helper from 'fastify-cli/helper.js'
import path from 'path'
// config for testing
export const config = () => {
return {}
}
export const build = async (t) => {
const argv = [
path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'app.js')
]
const app = await helper.build(argv, config())
t.teardown(app.close.bind(app))
return app
}
root.test.js
import { auth, build } from '../helper.js'
import { test } from 'tap'
test('requests the "/" route', async t => {
t.plan(1)
const app = await build(t)
const token = app.jwt.sign({ ... }) //-> jwt is undefined
const res = await app.inject({
method: 'GET',
url: '/'
})
t.equal(res.statusCode, 200, 'returns a status code of 200')
})
The issue is that your application diagram looks like this:
and when you write const app = await build(t) the app variable points to Root Context, but Your app.js contains the jwt decorator.
To solve it, you need just to wrap you app.js file with the fastify-plugin because it breaks the encapsulation:
import fp from 'fastify-plugin'
export default fp(async (fastify, opts) => { ... })
Note: you can visualize this structure by using fastify-overview (and the fastify-overview-ui plugin together:

React context API when wrapped around the main app component in index.js file it's display nothing

I am learning about context API. reducer.js:
export const initialState = {
user: null,
playlist: [],
playing: false,
item: null
};
const reducer = (state, action) => {
console.log(action);
switch(action.type) {
case 'SET_USER':
return {
...state,
user: action.user
}
default:
return state;
}
}
export default reducer;
DataLayer.js:
import React, {createContext, useContext, useReducer} from "react";
export const DataLayerContext = createContext();
export const DataLayer = ({initialState, reducer, children}) => {
<DataLayerContext.Provider value={useReducer(reducer, initialState)}>
{children}
</DataLayerContext.Provider>
};
now I am wrapping my App component around it in the index.js file
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { DataLayer } from './DataLayer';
import reducer, { initialState } from './reducer';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// <React.StrictMode>
<DataLayer initialState={initialState} reducer={reducer}>
<App />
</DataLayer>
// </React.StrictMode>
);
reportWebVitals();
but after doing this on the browser it display nothing no error not a single component that I have created is displayed but when I unwrapped the component it works fine
I also encountered the same issue and this is what worked for me …
The component (DataLayer.js) is not returning anything, so you have to change it from this:
export const DataLayer = ({initialState, reducer, children}) => {
<DataLayerContext.Provider value={useReducer(reducer, initialState)}>
{children}
</DataLayerContext.Provider>
};
to this:
export const DataLayer = ({ initialState, reducer, children }) => {
return (
<DataLayerContext.Provider value={useReducer(reducer, initialState)}>
{children}
</DataLayerContext.Provider>
)
};

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.

How to get UserId from localStorage

I'm trying to build a blog, on a certain end point I want to list all the blogs created by specific user only, so i have stored the user info in Local Storage and I'm trying to get the username of the same, so that i can send the username to backend to get the blogs posted by that specific username.
set cookie
import cookie from 'js-cookie';
export const setCookie = (key, value) => {
if (process.browser) {
cookie.set(key, value, {
expires: 1
});
}
};
get cookie
// get cookie
export const getCookie = key => {
if (process.browser) {
return cookie.get(key);
}
};
set LocalStorage
export const setLocalStorage = (key, value) => {
if (process.browser) {
localStorage.setItem(key, JSON.stringify(value));
}
};
isAuth action
export const isAuth = () => {
if(process.browser ) {
const cookieChecked = getCookie('token')
if(cookieChecked){
if(localStorage.getItem('user')){
return JSON.parse(localStorage.getItem('user'))
}else{
return false;
}
}
}
}
This is the Info that I have stored in the Local Storage
{_id: "5ece659149421c1f18c0c756", username: "2asjvwxkq", name: "test3", email: "test3#gmail.com", role: 0}
email: "test3#gmail.com"
name: "test3"
role: 0
username: "2asjvwxkq"
_id: "5ece659149421c1f18c0c756"
I tried doing const username = isAuth() && isAuth().username
But it is returning undefined
I want to send the username as a props to a component but the variable username is getting me undefined value.
Update :
JSON.stringify(username) gives me the username but i am unable to send the same to backend
Try doing
Suggestion 1 :
const username = isAuth() ? isAuth().username : ''
or use useEffect() wherever necessary .
Suggestion 2 :
Instead of sending the props , directly call the function
isAuth() inside the component you wanted to send in the first place ( if doing so won't affect anything ) and just to debug try doing JSON.stringify(user)
Hı
I made simple for you. I think problem is json.parse method.
look my example
export const isAuth = () => {
if (localStorage.getItem('user')) {
return localStorage.getItem('user');
} else {
return false;
}
}
and in my app.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {isAuth} from './isAuth'
function App() {
localStorage.setItem('user', 'bahtiyar');
const userName=isAuth();
return (
<div className="App">
<div>{userName}</div>
</div>
);
}
export default App;
look result:

Resources