N
Nuxt4d ago
Dante

Unable to fetch data from database

I am currently running localy and I am trying to fetch from my database, but all i get is undefined
// my middleware in server
import { auth } from "~/lib/auth";

export default defineEventHandler(async (event) => {
const session = await auth.api.getSession({
headers: event.headers,
});
event.context.user = session?.user || null;

if (event.path.startsWith("/dashboard")) {
if (!session?.user) {
return sendRedirect(event, "/auth/sign-in", 302);
}
}
});
// my middleware in server
import { auth } from "~/lib/auth";

export default defineEventHandler(async (event) => {
const session = await auth.api.getSession({
headers: event.headers,
});
event.context.user = session?.user || null;

if (event.path.startsWith("/dashboard")) {
if (!session?.user) {
return sendRedirect(event, "/auth/sign-in", 302);
}
}
});
// checks if user is authenticated
import type { EventHandler } from "h3";
export default function defineAuthHandler(handler: EventHandler) {
return defineEventHandler(async (event) => {
if (!event.context.user) {
return sendError(
event,
createError({
statusCode: 401,
statusMessage: "Unauthorised",
}),
);
}
return handler(event);
});
}
// checks if user is authenticated
import type { EventHandler } from "h3";
export default function defineAuthHandler(handler: EventHandler) {
return defineEventHandler(async (event) => {
if (!event.context.user) {
return sendError(
event,
createError({
statusCode: 401,
statusMessage: "Unauthorised",
}),
);
}
return handler(event);
});
}
import { and, eq } from "drizzle-orm";
import db from "..";
import { wellness } from "../schema";

export async function getWellness(userId: number) {
return db.query.wellness.findFirst({
where: eq(wellness.userId, userId),
});
}
import { and, eq } from "drizzle-orm";
import db from "..";
import { wellness } from "../schema";

export async function getWellness(userId: number) {
return db.query.wellness.findFirst({
where: eq(wellness.userId, userId),
});
}
// api/wellness/index.get.ts
import { getWellness } from "~/lib/db/queries/wellness";
import defineAuthHandler from "~/utils/define-auth-handler";

export default defineAuthHandler(async (event) => {
return getWellness(event.context.user.id);
});
// api/wellness/index.get.ts
import { getWellness } from "~/lib/db/queries/wellness";
import defineAuthHandler from "~/utils/define-auth-handler";

export default defineAuthHandler(async (event) => {
return getWellness(event.context.user.id);
});
5 Replies
kapa.ai
kapa.ai4d ago
Hi @Dante. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The snippets you shared are structurally fine for protecting an API route with server middleware + an auth wrapper, so the "undefined" is almost certainly coming from missing event.context.user (or event.context.user.id) rather than from the Drizzle query itself. From the Nuxt side, there are two key points relevant in the docs/issues: 1. Route middleware vs API routes
Nuxt route middleware only runs for page navigation (Vue Router), not for API routes; API routes are handled by Nitro/h3 defineEventHandler in a separate runtime.[Route middleware]
That means your auth logic for /api must live in server middleware or inside handlers themselves — which you are already doing. 2. Session/auth patterns
The official session/auth recipe uses a server middleware to attach the user to event.context, and then API handlers read from event.context.user (or reject with 401) — just like your defineAuthHandler does.[Session recipe] Given that, the most likely causes for undefined are: - auth.api.getSession({ headers: event.headers }) is returning null (no session), so event.context.user becomes null. - Your /api/wellness call is not actually going through the server middleware (e.g. middleware is not registered / wrong path matching). - You’re assuming event.context.user.id exists, but the session.user object doesn’t have id (maybe it’s userId, sub, or something else). To debug, log directly in the API handler:
// api/wellness/index.get.ts
import { getWellness } from "~/lib/db/queries/wellness";
import defineAuthHandler from "~/utils/define-auth-handler";
export default defineAuthHandler(async (event) => {
console.log('ctx.user in /api/wellness:', event.context.user);
return getWellness(event.context.user?.id);
});
// api/wellness/index.get.ts
import { getWellness } from "~/lib/db/queries/wellness";
import defineAuthHandler from "~/utils/define-auth-handler";
export default defineAuthHandler(async (event) => {
console.log('ctx.user in /api/wellness:', event.context.user);
return getWellness(event.context.user?.id);
});
If ctx.user (or ctx.user.id) is undefined: - Add a log in your server middleware to see what auth.api.getSession returns and ensure the middleware is actually executed for /api/wellness:
export default defineEventHandler(async (event) => {
const session = await auth.api.getSession({ headers: event.headers });
console.log('session in middleware:', session);
event.context.user = session?.user || null;
});
export default defineEventHandler(async (event) => {
const session = await auth.api.getSession({ headers: event.headers });
console.log('session in middleware:', session);
event.context.user = session?.user || null;
});
- Confirm that the middleware file is in server/middleware/ and that its filename/path matches how Nitro applies it globally, and that your client is hitting /api/wellness (not /wellness). If the session object is present but the ID field is different, adjust:
return getWellness(Number(event.context.user.userId)); // or whatever field your auth lib provides
return getWellness(Number(event.context.user.userId)); // or whatever field your auth lib provides
If you need more detail on how the auth helper populates session.user, that’s outside what the Nuxt docs/issues here describe; the knowledge sources don’t include that implementation, so I can’t say more about its structure or correctness. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@332238302457495564> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Dante
DanteOP4d ago
@kapa.ai as I was going through my codebase i noticed i had commneted out this hook in my auth file
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/get-session") {
if (!ctx.context.session) {
return ctx.json({
session: null,
user: null,
});
}
return ctx.json(ctx.context.session);
}
}),
},
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/get-session") {
if (!ctx.context.session) {
return ctx.json({
session: null,
user: null,
});
}
return ctx.json(ctx.context.session);
}
}),
},
uncommented and data is now fethcable although, i am now having the issure of my page being blank befor the data is loaded
kapa.ai
kapa.ai4d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You’re now getting data because your auth/session endpoint works again, but the “blank page before data is loaded” is just the default behavior: the page renders before your async data has finished. From the provided sources, there are three common Nuxt 3 patterns to avoid a blank screen: --- 1. Gate rendering with a loading flag Use a simple v-if and the page:finish hook to only show content after the initial navigation is done.[All requests hook]
<!-- app.vue -->
<template>
<div>
<div v-if="loading" class="fixed inset-0 z-50 bg-white">
<!-- your loader -->
Loading...
</div>
<NuxtPage v-else />
</div>
</template>

<script setup lang="ts">
const nuxtApp = useNuxtApp()
const loading = ref(true)

nuxtApp.hook('page:finish', () => {
loading.value = false
})
</script>
<!-- app.vue -->
<template>
<div>
<div v-if="loading" class="fixed inset-0 z-50 bg-white">
<!-- your loader -->
Loading...
</div>
<NuxtPage v-else />
</div>
</template>

<script setup lang="ts">
const nuxtApp = useNuxtApp()
const loading = ref(true)

nuxtApp.hook('page:finish', () => {
loading.value = false
})
</script>
This only waits for the route to be “ready”, not for every image or external resource, but it removes the initial white flash.[Nuxt loader limits] --- 2. Use <client-only> or conditional rendering around data If you only want to show a section once the data is there:
<template>
<div>
<div v-if="!data">Loading...</div>
<div v-else>
<!-- render data -->
</div>
</div>
</template>

<script setup lang="ts">
const { data } = await useFetch('/api/wellness')
</script>
<template>
<div>
<div v-if="!data">Loading...</div>
<div v-else>
<!-- render data -->
</div>
</div>
</template>

<script setup lang="ts">
const { data } = await useFetch('/api/wellness')
</script>
This is similar to the pattern shown for useAsyncData and redirects: they check data before rendering protected content.[401 redirect handling] --- 3. Use the loading indicator composable If you want to tie your own loader to Nuxt’s loading indicator, you can control it via useLoadingIndicator().isLoading and set it to false when your important resource has loaded.[Loading indicator control]
<script setup lang="ts">
const loadingIndicator = useLoadingIndicator()
loadingIndicator.isLoading.value = true

const { data } = await useFetch('/api/wellness')

watch(data, (val) => {
if (val) loadingIndicator.isLoading.value = false
})
</script>
<script setup lang="ts">
const loadingIndicator = useLoadingIndicator()
loadingIndicator.isLoading.value = true

const { data } = await useFetch('/api/wellness')

watch(data, (val) => {
if (val) loadingIndicator.isLoading.value = false
})
</script>
--- If your app is SPA-only (ssr: false) and you want to avoid the initial white screen entirely, there is an experimental fix that keeps the SPA loader visible until after hydration, configurable via app.spaLoaderTag, app.spaLoaderAttrs and an experimental flag. [SPA loader fix] However, the exact config keys are only partially described in the source, so I can’t give a full, reliable example beyond noting that this exists. In short: wrap your page or critical sections in a v-if based on either page:finish, your fetch result, or a combination, so you never render a completely empty layout while data is loading. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@332238302457495564> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?