Svelte客户端获取Bearer Token遇导入错误,求可行实现方案
Your original approach fails because any file importing server-only code (like $lib/server/fetchaccesstoken) can't be imported into client-side components (e.g., +layout.svelte). Here are two clean, maintainable ways to solve this:
Solution 1: Centralized Server-Side Fetch + Layout Data Prop
This is the recommended approach—fetch the token once on the server, cache it, and pass it to all client components via the layout.
Step 1: Add Caching to Your Server Token Function
Update $lib/server/fetchaccesstoken.ts to cache the token (avoids repeated calls to the auth server):
let cachedToken: { value: string; expiresAt: number } | null = null; export async function fetchAccessToken() { // Return cached token if it's still valid if (cachedToken && Date.now() < cachedToken.expiresAt) { return cachedToken.value; } // Fetch new token from auth server const response = await fetch('https://your-auth-server/token', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${import.meta.env.VITE_API_KEY}` // Your API key here }, body: JSON.stringify({ // Add your auth request parameters (e.g., grant_type, scope) }) }); if (!response.ok) throw new Error('Failed to fetch access token'); const data = await response.json(); // Cache token with expiry (subtract 1 minute to avoid edge cases) cachedToken = { value: data.access_token, expiresAt: Date.now() + (data.expires_in * 1000) - 60000 }; return cachedToken.value; }
Step 2: Fetch Token in Root Layout Server Load
Create +layout.server.ts in your root directory to fetch the token once and pass it to the client:
import { fetchAccessToken } from '$lib/server/fetchaccesstoken'; export async function load() { return { accessToken: await fetchAccessToken() }; }
Step 3: Access Token in Client Layout
In +layout.svelte, receive the token via the data prop and make it available to child components (use a store for easy access):
<script> export let data; import { writable } from 'svelte/store'; // Create a store to share the token with child components export const accessTokenStore = writable(data.accessToken); </script> <!-- Your layout markup here -->
Use Token in Child Components
Import the store from the layout in any client component:
<script> import { accessTokenStore } from '$layout'; let accessToken; accessTokenStore.subscribe(token => accessToken = token); </script> {#if accessToken} <!-- Make API requests using the token --> <button on:click={() => fetch('https://api.example.com/data', { headers: { 'Authorization': `Bearer ${accessToken}` } })}>Fetch Data</button> {/if}
Solution 2: API Route for Client-Side Token Refresh (Optional)
If you need to refresh the token without reloading the page, create a server-side API route that returns the token:
Create routes/api/token/+server.ts
import { fetchAccessToken } from '$lib/server/fetchaccesstoken'; import type { RequestHandler } from './$types'; export const GET: RequestHandler = async () => { const token = await fetchAccessToken(); return new Response(JSON.stringify({ token }), { headers: { 'Content-Type': 'application/json' } }); };
Call the Route from Client
In your client code, fetch a new token when needed:
async function refreshToken() { const response = await fetch('/api/token'); const { token } = await response.json(); accessTokenStore.set(token); }
Key Notes
- Your API key stays secure in server-only code, never exposed to the client.
- The cached token reduces redundant calls to your auth server.
- This approach avoids duplicating code across multiple
+page.server.jsfiles.
内容的提问来源于stack exchange,提问作者vasculum

