I have a custom express server that I'm using with NextJS.
Everything works just fine when I'm developing locally, but when I deploy to Vercel, I catch 404s whenever I try to access my backend API.
What could be going wrong? Here's my server.ts:
import express from 'express';
import next from 'next';
import bodyParser from 'body-parser';
import { connectDbs } from './config/db';
import { listingsRouter } from './routes';
const PORT = process.env.PORT || 3003;
const dbs = ['mydb'];
const dev = process.env.NODE_DEV !== 'production';
const nextApp = next({ dev });
const handle = nextApp.getRequestHandler();
const applyMiddleware = (app) => {
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
};
const applyRoutes = (app) => {
app.use('/api/listings', listingsRouter);
};
const startServer = async () => {
await nextApp.prepare();
const app = express();
applyMiddleware(app);
applyRoutes(app);
app.get('*', (req, res) => handle(req, res));
await connectDbs(dbs);
app.listen(PORT, () => console.log(`App listening on port ${PORT}`));
};
startServer();
The Next.js documentation for custom servers:
A custom server cannot be deployed on Vercel, the platform Next.js was made for.
Source: Custom Server
Even though you can't deploy a custom server, you generally don't need to do so because Next.js supports Serverless Functions using the /pages/api directory.
Source: API Routes
Also take a look at this Guide explaining how to convert custom Next.js server to routes:
Source: Server to Routes Guide
Related
image of the error I am trying to build nft search app that when you give adress it finds the nfts that a wallet has. but i am using alchemy and don't want to expose api key. Don't know backend, using next.js.
my backend code:
const express = require("express");
const axios = require("axios");
const dotenv = require("dotenv");
dotenv.config();
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/api", (req, res) => {
const apiKey = process.env.API_KEY;
const owner = req.query.owner;
const baseURL = `https://eth-mainnet.g.alchemy.com/nft/v2/${apiKey}/getNFTs/`;
const fetchURL = `${baseURL}?owner=${owner}`;
axios
.get(fetchURL)
.then((response) => {
res.json(response.data);
})
.catch((error) => {
res.json({ error: error.message });
});
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
my frontend
const fetchNFTs = async () => {
const response = await fetch(`/api?${wallet}`);
const nfts = await response.json();
setNFTs(nfts);
};
I tried chat gpt, serverless function but I failed to achieve results
Is this a separate Express server? By default Next.js runs on port 3000, and that's why you're making a GET to localhost:3000. But if your Next.js server is started, your custom Express server would pick a different port because the 3000 is going to be taken. So your const response = await fetch('/api?${wallet}'); will result in a 404 because there's no such route in your Next.js app.
You could move your function into a Next.js API handler if you want your backend and frontend to live under the same port. Also you wouldn't need to manually create the express server and take care of the dotenv since Next.js is going to take care of that for you.
I'm a bit of a JS and Vue noob and I'm trying to figure out how to run my Vue app and a websocket server for that app on the same port 80 (to later be put behind a reverse proxy). I am able to get this working as a simple http server with express, but I can't figure out how to do it with my Vue use case.
My goal is to run a single main.js with npm run serve and bind the websocket server and routes I'm setting up with the vue router all in one application, on the same port.
Here is the example for the simple HTTP case:
server.js
'use strict';
let fs = require('fs');
let express = require('express');
let app = express();
let bodyParser = require('body-parser');
let WSServer = require('ws').Server;
let server = require('http').createServer();
let PORT = 8080
app.use(bodyParser.json());
// regular HTTP request and response
app.get('/', function(req, res) {
console.log('Get index');
fs.createReadStream('./index.html').pipe(res);
});
app.post('/', function(req, res) {
let message = req.body.message;
console.log('Regular POST message: ', message);
return res.json({ answer: 42 });
});
module.exports = app;
// Create web socket server on top of a regular http server
let wss = new WSServer({ server: server });
// Also mount the app here
server.on('request', app);
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log(`received: ${message}`);
ws.send(JSON.stringify({ answer: 42 }));
});
});
server.listen(PORT, function() {
console.log(`http/ws server listening on ${PORT}`);
});
But my question is how can I modify the default main.js vue file to do this?
import { createApp } from 'vue';
import App from './App.vue';
---
import router from './router';
import store from './store';
createApp(App).use(store).use(router).mount('#app');
hope you are well!
I have an issue when trying to serve a create-react-app using an express.js server with Node.js backend.
For backend I have some routes defined to create an API with express and prisma, connecting to a database.
For frontend I used a react based app, Axios to fetch data from backend and react-router-dom V6 for client routing (BrowserRouter). To serve the files I created a build folder from React (in the .env file I have INLINE_RUNTIME_CHUNK set to false) with the npm run build script.
The files are generated correctly for the build and from the express app I attach these interface files with the separate route along with the API routes I have already defined. Everything goes okay, the interface opens, the API works but only if I navigate using components in the interface.
When I refresh a page in the browser, the application suddenly stops rendering, under a white screen. In the console area, in DevTools all kinds of loading errors appear which I will attach below with a print screen. The same thing happens if I try to navigate to another route in the browser bar.
I want to launch the application with express on an IP from a local machine. The server code is attached below with the required routes and middleware. So is the axios code and the routing in the interface. Has anyone else had problems like this?
import axios from 'axios';
const publicFetch = axios.create({
baseURL: "http://ABC.ABC.0.ABC:3002/api-v1"
});
export default publicFetch;
An this is the server code:
'use strict';
require('dotenv').config();
const express = require('express');
const path = require('path');
const morgan = require('morgan');
const helmet = require('helmet');
const cors = require('cors');
const compression = require('compression');
const SERVER_PORT = process.env.SERVER_PORT;
// ================================================
// Server routes/controllers
// ================================================
const { createUser, getAllUsers, getSingleUser, updateUser, deleteUser, loginUser } = require('./routes/user-routes');
const { createDepartment, getAllDepartments, getSingleDepartment, updateDepartmentData, deleteDepartment } = require('./routes/department-routes');
const whitelist = ['ABC.ABC.0.ABC'];
const corsOptions = {
origin: function (origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
// ================================================
// Express App
// ================================================
const app = express();
app.enable('trust proxy');
// CORS
app.use(cors(corsOptions));
// JSON
app.use(express.json());
// ================================================
// static serve from server and client build
// ================================================
app.use(express.static(path.join(__dirname, '../client/build')));
// compress responses
// use compressions middleware for static files and res send
app.use(compression());
// ================================================
// Middleware
// ================================================
// morgan logs middleware
app.use(morgan('dev'));
// helmet security headers
app.use(helmet());
// disable Express mention in reasponse header
app.disable('x-powered-by');
// ===============================================
// User Endpoints
// ================================================
app.post('/api-v1/user', createUser);
app.get('/api-v1/user', getAllUsers);
app.get('/api-v1/user/:id', getSingleUser);
app.put('/api-v1/user/:id', updateUser);
app.delete('/api-v1/user/:id', deleteUser);
app.post('/api-v1/user/login', loginUser);
// ================================================
// Department Endpoints
// ================================================
app.post('/api-v1/department', createDepartment);
app.get('/api-v1/department', getAllDepartments);
app.get('/api-v1/department/:id', getSingleDepartment);
app.put('/api-v1/department/:id', updateDepartmentData);
app.delete('/api-v1/department/:id', deleteDepartment);
// Handles any requests that don't match the ones above
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
// ================================================
// Server start
// ================================================
app.listen(SERVER_PORT, "ABC.ABC.0.ABC", () => {
console.log(`Server running and listening on port ${SERVER_PORT}...`);
});
The errors that I get:
error in console
I'm currently setting up a custom Node server with Next.js. The fact that I'm using Next.js shouldn't make any difference though.
In previous apps, I've always used mongoose.connection.once('open', callback) to start listening only when the database is open. This time, it's not working.
This is my connection configuration file:
import mongoose from 'mongoose';
import { MONGO_URI } from '../constants'; // looks like 'mongodb://localhost/my-db' in development
mongoose
.connect(MONGO_URI, () => {
try {
console.log(`Connected to ${MONGO_URI} with Mongoose.`);
} catch (err) {
throw err;
}
})
export default mongoose.connection;
I am importing this and using it in my main.ts file like so:
import express from 'express';
import next from 'next';
import * as dotenv from 'dotenv';
import logger from 'morgan';
import compression from 'compression';
import helmet from 'helmet';
import rateLimiter from './config/rateLimiter';
import db from './config/connection'; // This is the config file
dotenv.config();
const PORT = process.env.PORT || '8000';
const dev = process.env.NODE_ENV !== 'production';
const nxt = next({ dev });
const handle = nxt.getRequestHandler();
nxt.prepare().then(() => {
const app = express();
app.enable('trust proxy');
app.use(logger('dev'));
app.use(helmet());
app.use(rateLimiter);
app.use(compression());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
db.once('open', () => {
app.listen(PORT, () => {
// This log never happens
console.log(`Listening on port ${PORT}.`);
});
});
});
It's extremely strange, because "Connected to mongodb://localhost/my-db with Mongoose." is in fact logged when using the code above, but the express app simply never listens; however, when I remove the app.listen out of the db.once callback function, "Listening on port 8000" does of course get logged.
I'm stumped. Why isn't the 'open' event firing? I've verified that mongo is working locally through the Mongo shell, and this same exact code was working when I had the folder which these files are in (server) separate from the Next.js app (when I was still debating which type of view layer to write).
The issue is not compatibility. I'm running Mongo 5.0.5, and Mongoose 6.2.5, which should be find according to this
Please don't link to some other question. None of these have helped:
My mongo is compatible
mongoose.connect doesn't return a promise
The comment from #Shivam Sood was correct. The db.once('open') was being called too late. Once nxt.prepare had happened, the db was already opened.
Here is the modified code:
import express from 'express';
import next from 'next';
import * as dotenv from 'dotenv';
import logger from 'morgan';
import compression from 'compression';
import helmet from 'helmet';
import rateLimiter from './config/rateLimiter';
import db from './config/connection';
dotenv.config();
const PORT = process.env.PORT || '8000';
const dev = process.env.NODE_ENV !== 'production';
const nxt = next({ dev });
const handle = nxt.getRequestHandler();
db.once('open', () => { // Listen for 'open' event before nxt.prepare, and before listening on PORT
nxt.prepare().then(() => {
const app = express();
app.enable('trust proxy');
app.use(logger('dev'));
app.use(helmet());
app.use(rateLimiter);
app.use(compression());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.get('*', (req, res) => {
return handle(req, res);
});
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}.`);
});
});
});
I created a Nuxt app with express backend, and i have registered some api routes. When i run locally as production npm run build && npm run start it works just fine.
Here it is working locally
However when i run it with heroku heroku local web all the API routes throw a 404.
Here it doesn't work with heroku
Here is my server code
require('dotenv').config();
const express = require('express');
const consola = require('consola');
const { Nuxt, Builder } = require('nuxt');
const bodyParser = require('body-parser');
const session = require('express-session');
const mongoose = require('mongoose');
const cors = require('cors');
mongoose.Promise = Promise;
mongoose.set('useFindAndModify', false);
mongoose.connect(process.env.MONGODB_CONNECTION_STRING);
const app = express();
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use('/api', require('./routes'));
// session
app.use(
session({
sessionDataHere
})
);
// enable cors
app.use(cors());
// body parser
app.use(bodyParser.json());
app.use((error, req, res, next) => {
console.error(error.response);
res.status(500).send(error);
});
// Give nuxt middleware to express
app.use(nuxt.render);
// Listen the server
app.listen(port, host);
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
});
}
start();
And here is the index of my routes
const { Router } = require('express');
const authRouter = require('./auth');
const videoRouter = require('./video');
const baseRouter = Router();
baseRouter.use('/', authRouter);
baseRouter.use('/', videoRouter);
baseRouter.get('/test', (req, res) => res.send('This is working!'));
module.exports = baseRouter;
Maybe i'm missing something on the heroku configuration? Thanks!
Ok, i fixed it. I had to change my procfile. It was running nuxt start not npm start so it wasn't running any of the server side code