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
Any idea as to why? i've tried with
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
}
path
, root
...4 Replies
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}`);
});
iirc serveStatic is served from the cwd, log the path onNotFound to double check
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 !!PS: if you are using
for the url
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);
/something/whatever
, it will try to serve ./something/whatever
and not ./whatever
this is a general issue with how globs behave