H
Hono4mo ago
Lenskha

serveStatic() doesn't serve files

I have a simple hono server with a trpc router. Unfortunately the bundled frontend isn't being served by the hono server. And API calls to the /trpc route are intercepted by the app.get('*', serveStatic({ root: '../../front/dist/index.html'})) route and also return a "404 not found". Here is the full code
import { Hono } from 'hono'
import { trpcServer } from '@hono/trpc-server'
import { router } from './trpc';
import { cors } from 'hono/cors'
import { Session, sessionMiddleware, CookieStore } from 'hono-sessions'
import { serveStatic } from 'hono/bun'

import { imagesRouter } from './routes/images';

type sessionData = {
userId?: string;
isAuthenticated?: boolean;
username?: string;
}

const app = new Hono<{
Variables: {
session: Session<sessionData>,
}
}>()

const store = new CookieStore()

app.use('*', cors({
origin: 'http://localhost:5173',
credentials: true
}));

app.use('*', sessionMiddleware({
store,
sessionCookieName: 'session',
encryptionKey: 'password_at_least_32_characters_long',
expireAfterSeconds: 900,
}))

const appRouter = router({
images: imagesRouter,
})

export type AppRouter = typeof appRouter;

app.use('/trpc', trpcServer({
router: appRouter,
createContext(_opts, c) {
console.log('in createContext')
return {
session: c.get('session')
};
}
}));


app.get('/api/check-session', (c) => {
const session = c.get('session')
console.log('Session data:', {
userId: session.get('userId'),
isAuthenticated: session.get('isAuthenticated'),
username: session.get('username')
});
return c.json({
userId: session.get('userId'),
isAuthenticated: session.get('isAuthenticated'),
username: session.get('username')
});
});

app.get('*', serveStatic({ root: '../../front/dist/index.html'}))

export default {
port: 3000,
fetch: app.fetch
}
import { Hono } from 'hono'
import { trpcServer } from '@hono/trpc-server'
import { router } from './trpc';
import { cors } from 'hono/cors'
import { Session, sessionMiddleware, CookieStore } from 'hono-sessions'
import { serveStatic } from 'hono/bun'

import { imagesRouter } from './routes/images';

type sessionData = {
userId?: string;
isAuthenticated?: boolean;
username?: string;
}

const app = new Hono<{
Variables: {
session: Session<sessionData>,
}
}>()

const store = new CookieStore()

app.use('*', cors({
origin: 'http://localhost:5173',
credentials: true
}));

app.use('*', sessionMiddleware({
store,
sessionCookieName: 'session',
encryptionKey: 'password_at_least_32_characters_long',
expireAfterSeconds: 900,
}))

const appRouter = router({
images: imagesRouter,
})

export type AppRouter = typeof appRouter;

app.use('/trpc', trpcServer({
router: appRouter,
createContext(_opts, c) {
console.log('in createContext')
return {
session: c.get('session')
};
}
}));


app.get('/api/check-session', (c) => {
const session = c.get('session')
console.log('Session data:', {
userId: session.get('userId'),
isAuthenticated: session.get('isAuthenticated'),
username: session.get('username')
});
return c.json({
userId: session.get('userId'),
isAuthenticated: session.get('isAuthenticated'),
username: session.get('username')
});
});

app.get('*', serveStatic({ root: '../../front/dist/index.html'}))

export default {
port: 3000,
fetch: app.fetch
}
Any idea as to why? i've tried with path, root...
4 Replies
Lenskha
LenskhaOP4mo ago
same code with express works flawlessly :
import express from 'express';
import { router } from './trpc';
import cors from 'cors';
import path from 'path';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import { imagesRouter } from './routes/images';
import session from 'express-session';
import { MemoryStore } from 'express-session';

const app = express();


app.use(cors({
origin: 'http://localhost:5173',
credentials: true
}));


const sessionStore = new MemoryStore();

app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));


app.use(express.json());


app.use(express.static(path.join(__dirname, '../../front/dist')));


const appRouter = router({
images: imagesRouter,
})

export type AppRouter = typeof appRouter;


declare module 'express-session' {
interface SessionData {
userId: string;
isAuthenticated: boolean;
username: string;
}
}

app.get('/api/check-session', (req, res) => {
res.json({
isAuthenticated: req.session.isAuthenticated || false,
userId: req.session.userId || null,
username: req.session.username || null
});
});

app.post('/api/login', (req, res) => {
const { username, password } = req.body;
console.log('Login attempt:', username, password);


if (username === 'admin' && password === 'p') {
req.session.userId = '1';
req.session.isAuthenticated = true;
req.session.username = username;
res.json({ success: true, message: 'Login successful' });
} else if (username === 'user' && password === 'p') {
req.session.userId = '2';
req.session.isAuthenticated = true;
req.session.username = username;
res.json({ success: true, message: 'Login successful' });
} else {
res.status(401).json({ success: false, message: 'Invalid credentials' });
}
});

app.post('/api/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
res.status(500).json({ success: false, message: 'Logout failed' });
} else {
res.json({ success: true, message: 'Logout successful' });
}
});
});


app.use('/trpc', createExpressMiddleware({
router: appRouter,
createContext({ req }) {
console.log('req.session', req.session.userId, req.session.isAuthenticated, req.session.username);

return {
session: req.session
};
}
}));

app.get('/{*zzz}', (req, res) => {
res.sendFile(path.join(__dirname, '../../front/dist', 'index.html'));
});

const port = 3000;

app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
import express from 'express';
import { router } from './trpc';
import cors from 'cors';
import path from 'path';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import { imagesRouter } from './routes/images';
import session from 'express-session';
import { MemoryStore } from 'express-session';

const app = express();


app.use(cors({
origin: 'http://localhost:5173',
credentials: true
}));


const sessionStore = new MemoryStore();

app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));


app.use(express.json());


app.use(express.static(path.join(__dirname, '../../front/dist')));


const appRouter = router({
images: imagesRouter,
})

export type AppRouter = typeof appRouter;


declare module 'express-session' {
interface SessionData {
userId: string;
isAuthenticated: boolean;
username: string;
}
}

app.get('/api/check-session', (req, res) => {
res.json({
isAuthenticated: req.session.isAuthenticated || false,
userId: req.session.userId || null,
username: req.session.username || null
});
});

app.post('/api/login', (req, res) => {
const { username, password } = req.body;
console.log('Login attempt:', username, password);


if (username === 'admin' && password === 'p') {
req.session.userId = '1';
req.session.isAuthenticated = true;
req.session.username = username;
res.json({ success: true, message: 'Login successful' });
} else if (username === 'user' && password === 'p') {
req.session.userId = '2';
req.session.isAuthenticated = true;
req.session.username = username;
res.json({ success: true, message: 'Login successful' });
} else {
res.status(401).json({ success: false, message: 'Invalid credentials' });
}
});

app.post('/api/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
res.status(500).json({ success: false, message: 'Logout failed' });
} else {
res.json({ success: true, message: 'Logout successful' });
}
});
});


app.use('/trpc', createExpressMiddleware({
router: appRouter,
createContext({ req }) {
console.log('req.session', req.session.userId, req.session.isAuthenticated, req.session.username);

return {
session: req.session
};
}
}));

app.get('/{*zzz}', (req, res) => {
res.sendFile(path.join(__dirname, '../../front/dist', 'index.html'));
});

const port = 3000;

app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
blair
blair4mo ago
iirc serveStatic is served from the cwd, log the path onNotFound to double check
Lenskha
LenskhaOP4mo ago
oooh indeed! I thought the root path needed to be relative to the current file :(( As for my /trpc route problem, I misread the docs and needed app.use('/trpc/*', trpcServer({... instead of /trpc Thank you very much !!
Arjix
Arjix4mo ago
PS: if you are using serveStatic or a sub-route and not the root level, it will not serve relative to the route, but it will use the absolute path e.g.
const app = new Hono();
const subRoute = new Hono();

subRoute.get("*", serveStatic({}));
app.route('/something', subRoute);
const app = new Hono();
const subRoute = new Hono();

subRoute.get("*", serveStatic({}));
app.route('/something', subRoute);
for the url /something/whatever, it will try to serve ./something/whatever and not ./whatever this is a general issue with how globs behave

Did you find this page helpful?