If you want to serve files using public folder in next js and you want to have upload ability you should use this custom server because next js wont serve dynamically created or uploaded images.
To counter this, we can use express' static method.
Package needed:
- express
- cross-env (optional, you might be able to use other lib like dotnev)
(methods tested on next js 14 using nextjs app)
See this server.ts below
import express, { Express, Request, Response } from "express";
import next from "next";
import path from "path";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const port = process.env.PORT || 3000;
app
.prepare()
.then(() => {
const server: Express = express();
// Serve static files from the "public" directory
const staticPath = dev ? path.join(__dirname, "public") : path.join(__dirname, "..", "public");
server.use(express.static(staticPath)); // all in the public dir
// alternatively you can "whitelist" some media maybe in certain folder
// server.use("/uploads/*", (req, res, next) => {
// express.static(staticPath + req.originalUrl)(req, res, next);
// });
console.log("Serving Static & watching uploads at: " + staticPath);
// Handle all other routes with Next.js
server.all("*", (req: Request, res: Response) => {
return handle(req, res);
});
server.listen(port, (err?: any) => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
})
.catch((err) => {
console.error(err.stack);
process.exit(1);
});then create this tsconfig for the server.ts. Name this tsconfig.server.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist",
"lib": ["es2019"],
"target": "es2019",
"isolatedModules": false,
"noEmit": false,
"moduleResolution": "node",
},
"include": ["server.ts"]
}Then create this nodemon config. (we are gonna use nodemon for development)
nodemon.json
{
"watch": ["server.ts"],
"exec": "ts-node --project tsconfig.server.json server.ts",
"ext": "js ts"
}
Lastly, add this scripts in your package.json
"scripts": {
"dev": "nodemon",
"build": "next build && tsc --project tsconfig.server.json",
"start": "cross-env NODE_ENV=production node dist/server.js",
},To explain, we use nodemon to watch our server.ts when in development.
To build the app, we build the next project and then compile our server.ts file.
When starting the app, we first load the env so it can be read properly using cross-env and then set our NODE_ENV to production.
Credits: