Structure design question about data share
Answered
Chilean jack mackerel posted this in #help-forum
Chilean jack mackerelOP
I have this
I'm exporting the
spotify.ts file that contains all the methods and data needed to use their API. It looks like thisimport { APIErrorReturn, SpotifyAPIReturn } from './../interfaces/spotifyInterfaces';
const SPOTIFY_WEB_API_HOST = "https://api.spotify.com/v1"
class SpotifyAPI {
private _access_token: string;
constructor(token?: string){
this._access_token = token || "";
}
setAccessToken(token: string){
console.log("✓ Setting access token: " + token)
this._access_token = token
}
getAccessToken(){
return this._access_token;
}
async getArtist(id: string){
return await callRequest({endpoint: "/artists/" + id, access_token: this._access_token}) as SpotifyAPIReturn
}
// ... other functions
export const SpotifyAPIClient = new SpotifyAPI();
}I'm exporting the
SpotifyAPIClient variable to use this globally to use these functions. But now I have a problem. Whenever I'm setting an access token, it's not set up properly on the client side. I mean, if I call setAccessToken on the serverSide code, the access_token is not set while calling functions in the clientSide. I understand that this can be because I set the value inside serverSide and these file states are not the same, but when I'm trying to update the access_token inside the layout component that is client sided the effect is the same. Now my question is, how can I properly setup this kind of structure?Answered by Chilean jack mackerel
//action.ts
'use server'
import { getCurrentServerSession } from "@/database/auth"
import { SpotifyAPIClient } from "@/database/spotify"
export async function getSpotifyAccessToken() {
return SpotifyAPIClient.getAccessToken()
}
export async function setSpotifyAccessToken() {
const b = await getCurrentServerSession()
if(b)
SpotifyAPIClient.setAccessToken(b.access_token)
else
SpotifyAPIClient.setAccessToken('')
}and now instead of getting access_token by
SpotifyAPIClient.getAccessToken() inside API request function I just call the const access_token = await getSpotifyAccessToken() and it works fine but only with this bug54 Replies
@Chilean jack mackerel I have this `spotify.ts` file that contains all the methods and data needed to use their API. It looks like this
ts
import { APIErrorReturn, SpotifyAPIReturn } from './../interfaces/spotifyInterfaces';
const SPOTIFY_WEB_API_HOST = "https://api.spotify.com/v1"
class SpotifyAPI {
private _access_token: string;
constructor(token?: string){
this._access_token = token || "";
}
setAccessToken(token: string){
console.log("✓ Setting access token: " + token)
this._access_token = token
}
getAccessToken(){
return this._access_token;
}
async getArtist(id: string){
return await callRequest({endpoint: "/artists/" + id, access_token: this._access_token}) as SpotifyAPIReturn
}
// ... other functions
export const SpotifyAPIClient = new SpotifyAPI();
}
I'm exporting the `SpotifyAPIClient` variable to use this globally to use these functions. But now I have a problem. Whenever I'm setting an access token, it's not set up properly on the client side. I mean, if I call `setAccessToken` on the serverSide code, the access_token is not set while calling functions in the clientSide. I understand that this can be because I set the value inside serverSide and these file states are not the same, but when I'm trying to update the access_token inside the layout component that is client sided the effect is the same. Now my question is, how can I properly setup this kind of structure?
I think you could try this
https://github.com/epicweb-dev/remember
https://github.com/epicweb-dev/remember
Chilean jack mackerelOP
well unfortunately it's not doing much, I mean the problem still occurs and access_token is not being set
i mean it's being set but the state is not the same across other files i think
export const SpotifyAPIClient = remember('SpotifyAPIClient', () => new SpotifyAPI());I understand that it could not have the same access_token state comparing to clientside and serverside version but I don't know why the clientside set and clientside get but in different files are not the same
what do you mean clientside set and clientside get?
you set it with server action on client side?
Chilean jack mackerelOP
I mean, I have for example an layout component which is server sided because it's server component
and I'm trying to set the access_token there, but while reading the access token inside the client component, the access_token is null because it has been set on the server and not on the client (i assume) but now my question is why while setting up the access_token on the client sided component, I can't get this access_token in the other client sided component, like the
//layout.tsx
export default async function RootLayout({
children,
}: {
children: React.ReactNode
}) {
async function applyAccessToken(){
console.log('applying')
const res = await getCurrentClientSession();
if(res){
SpotifyAPIClient.setAccessToken(res.access_token)
}
}
await applyAccessToken()
return (
<NextAuthProvider>
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
</NextAuthProvider>
)and I'm trying to set the access_token there, but while reading the access token inside the client component, the access_token is null because it has been set on the server and not on the client (i assume) but now my question is why while setting up the access_token on the client sided component, I can't get this access_token in the other client sided component, like the
SpotifyAPIClient instance is getting resetted every time.can you show the code where you set the token and get the token on client side?
Chilean jack mackerelOP
for example I have this
I know it's not good idea to just call the function like that but It's for testing anyways. And now I have my other client component for calling SpotifyAPI with previous set access_token but it's not there anymore.
and of course while calling
NextAuthProvider file for auth but It's client sided so I can use it to set the access_token//NextAuthProvider.tsx
"use client";
import { getCurrentClientSession } from "@/database/auth";
import { SpotifyAPIClient } from "@/database/spotify";
import { SessionProvider } from "next-auth/react";
type Props = {
children?: React.ReactNode;
};
export const NextAuthProvider = ({ children }: Props) => {
async function applyAccessToken(){
console.log('applying')
const res = await getCurrentClientSession();
if(res){
SpotifyAPIClient.setAccessToken(res.access_token)
}
}
applyAccessToken()
return (
<SessionProvider>
{children}
</SessionProvider>
)
};I know it's not good idea to just call the function like that but It's for testing anyways. And now I have my other client component for calling SpotifyAPI with previous set access_token but it's not there anymore.
//Component.tsx
"use client";
import { SpotifyAPIClient } from '@/database/spotify';
import React, { useEffect, useState } from 'react'
import TopListBox from './TopListBox';
const TopStatsContainer = () => {
const [myTopArtists, myTopArtistsSet] = useState([]);
async function fetchOverviewStats(){
const topArtists = SpotifyAPIClient.getMyTopArtists(3);
myTopArtistsSet((await topArtists).data.items)
}
useEffect(() => {
console.log(' - TopStatsContainer (useEffect): ', SpotifyAPIClient.getAccessToken())
fetchOverviewStats()
}, [])
return (
<div className='top__stats_container'>
<h1>My overwiew</h1>
<TopListBox title={"test"} data={myTopArtists} />
</div>
)
}and of course while calling
.getMyTopArtists() function I get the error because access_token is not set in this instance but I don't know why.Chilean jack mackerelOP
well now I know that, but I'm looking for an idea to make it work
use server action
// action.ts
'use server'
export async function getSpotifyAccessToken() {
return SpotifyAPIClient.getAccessToken()
}
export async function setSpotifyAccessToken(token) {
SpotifyAPIClient.setAccessToken(token)
}Chilean jack mackerelOP
well okaay, but can I use my already created SpotifyAPI class with other functions or I have to convert it into server actions?
where you gonna host it?
it is not gonna work too if you host it on some serverless platform
Chilean jack mackerelOP
ugh i thought about cloudflare
but you know that this access_token is not global for entire website but specific for every user that is trying to access API right?
why dont you store it on db?
@Chilean jack mackerel but you know that this access_token is not global for entire website but specific for every user that is trying to access API right?
I didn't know that but I know this is not gonna work
@Ray why dont you store it on db?
Chilean jack mackerelOP
well mostly because then I would have to check for the access_token in database everytime and refresh it if it's expired and the whole process would be longer
you could use redis for it
Chilean jack mackerelOP
So it's not possible to set the access_token once for entire page like in context and get this token from everywhere in the project right?
but you will lose the data if it restart
Chilean jack mackerelOP
I just want to remember the access_token while page is being used, on the any other visit it's gonna set the access_token again anyway.
what is the access_token for?
maybe cookie is a good place for it
Chilean jack mackerelOP
for being able to call requests for Spotify API
@Ray maybe cookie is a good place for it
Chilean jack mackerelOP
but do I have the access for cookies inside server sided code?
Chilean jack mackerelOP
well, I will try it then, thanks for the idea
Chilean jack mackerelOP
oh well I just realised that I if modify the SpotifyAPI class to get and set the access_token based on cookies using
next/headers then I can't use it inside client components :/pass it down from server component
and use server action to set on client side
Chilean jack mackerelOP
well, then I have to give up with idea of structure I already created in
spotify.tsChilean jack mackerelOP
I have to pass cookies with access_token for every client component and use it with api calls
@Ray ts
// action.ts
'use server'
export async function getSpotifyAccessToken() {
return SpotifyAPIClient.getAccessToken()
}
export async function setSpotifyAccessToken(token) {
SpotifyAPIClient.setAccessToken(token)
}
Chilean jack mackerelOP
okay I've done it using server actions, but I have one strange bug. When I'm logging out the access_token is still present so requests are going through and only after I refresh page the access_token is being nulled
did you clear it on cookie when you log out?
Chilean jack mackerelOP
//action.ts
'use server'
import { getCurrentServerSession } from "@/database/auth"
import { SpotifyAPIClient } from "@/database/spotify"
export async function getSpotifyAccessToken() {
return SpotifyAPIClient.getAccessToken()
}
export async function setSpotifyAccessToken() {
const b = await getCurrentServerSession()
if(b)
SpotifyAPIClient.setAccessToken(b.access_token)
else
SpotifyAPIClient.setAccessToken('')
}and now instead of getting access_token by
SpotifyAPIClient.getAccessToken() inside API request function I just call the const access_token = await getSpotifyAccessToken() and it works fine but only with this bugAnswer
Chilean jack mackerelOP
well i'm not doing anything with cookies right now
only server actions
<button onClick={() =>{ signOut()}}>logout</button> just using NextAuth functionI think you need to call
setSpotifyAccessToken when you do signoutChilean jack mackerelOP
maybe i just need to set the access_token manually to null after logging out
yeah
yeah it's working just fine now. Thank you very much for help!
I suggest you deploy it to cloudflare and see if it actually work before you continue developing it
I'm not sure is this going to work in any serverless platform
Chilean jack mackerelOP
well i deployed it on vercel, because nextauth has some problems while deploying on cfpages and the result that it's working but with other bugs
on the first login the access_token is not being set but after refresh it's doing fine, and after logout, the bug from above is back