How do I serve an express + vue app on same domain in Heroku - vue.js

I have a backend api in express and a front end in vue.js with typescript.
In my development environment, the front end talks to the backend via a proxy server defined in the vue.config.js file...
module.exports = {
devServer: {
proxy: {
"^/api": {
target: 'http://localhost:3002',
changeOrigin: true,
pathRewrite: {"^/api": "/"}
},
}
}
}
So, my front end expects to be served on [address] and it is expecting the express application to be at [address]/api on the SAME address/url.
I was able to achieve this on Digital Ocean App Platform by setting up 2 'components', a backend served from '/api' and a front end component with the Vue.js stuff in it served from '/' on the same domain.
How can I achieve this in Heroku?
I have seen a lot of people (e.g. https://www.youtube.com/watch?v=W-b9KGwVECs) suggest using a build step that builds the front end, copies the static assets back up into a 'public' folder and then uses express to serve the static assets as well as the rest api using this...
app.use(express.static(__dirname + '/public/'));
But is this really the best way to do it? Seems to me like now Express is responsible for the front end and the backend when they should be seperate concerns. Also, what about scaling the back vs the front?, does this work for CDNs?
This seems like such a common use case for something like Heroku, maybe I am just missing something obvious.

Related

http-proxy-middleware adds a '/' before query parameters

I want to consume an API using a proxy written in express. I use http-proxy-middleware for this. Here is the setup I have:
app.use(
createProxyMiddleware('/api', {
target: 'http://example.com/api/v2',
changeOrigin: true,
pathRewrite: {
'/api': '',
}
})
);
Then I make a request from postman or browser: GET http://localhost:8080/api/list?first=50
All I get from the API server is 404. I saw in the browser that the URL changes to http://localhost:8080/api/v2/list/?first=50 and I don't understand why.
All I want is to add an auth header which I managed to do using onProxyReq, but now I just want everything that comes after /api to be forwarded as is to http://example.com/api/v2.
I just got it to work. Turns out I had some things wrong. The first wrong thing target: 'http://example.com/api/v2' should be target: 'http://example.com'. Then, pathRewrite will rewrite anything it matches and redirect to the new path, so it ended up calling localhost:8080/api/list?first=50 and then localhost:8080/api/v2/list/?first=50. So with these 2 mistakes combined, in the end the API call would be example.com/api/v2/v2/list/?first=50 and that's clearly wrong. I replaced the target and I am now using /api/v2 as context for the proxy.
I would still like to call my proxy using localhost:8080/api/whatever and have it turned into example.com/api/v2/whatever, but it's just a nice to have.

Next.js serving static files that are not included in the build or source code

I have files that are not stored in a CDN and would like to serve them with Next.js. These files are not intended to be integrated into Next.js and should not be placed in the public folder. More files will be added and I want to avoid using a custom Next.js server to do simple file serving for images that are not available during building. Additionally, this application will only be deployed locally and using a CDN is overkill for this situation.
Currently, I use Express.js and a Next.js custom server to use express.static to serve files, but this ends up slowing down Next.js and adds lots of unnecessary complexity to my stack. I'd rather just use the Next.js CLI to run my app instead of reinventing the wheel.
Is there a simple way I can serve static files within Next.js and outside the public directory?
I posted this question and my own answer here on StackOverflow because I was unable to find a good tutorial on how to do this. Nearly every google search says to use a custom server or to just put your files in the public folder, which is not what I was looking for. Hopefully, others who are looking for the same thing may find it here.
Disclaimer: I do not use Vercel to publish my applications, and I do not know if this answer will be applicable to Next.js on Vercel.
Next.js allows API routes to be customized to support Node.js HTTP handlers, which means express can also be used within Next.js API routes.
Here is some code to utilize express.static on a Next.js API route.
// pages/api/images/[name].js
// Tell Next.js to pass in Node.js HTTP
export const config = {
api: { externalResolver: true }
}
import express from 'express';
const handler = express();
const serveFiles = express.static('./path/to/files');
handler.use(['/api/images', '/images'], serveFiles);
// ^ ^
// Multiple endpoints are passed. The first one is used when visiting /api/images.
// The second one is used when visiting /images using the middleware rewrite I mention below.
// express is just a function that takes (http.IncomingMessage, http.ServerResponse),
// which Next.js supports when externalResolver is enabled.
export default handler;
However to get around visiting this endpoint via /api/images/filename, you can use Next.js's new middleware to rewrite the request!
// pages/images/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(req) {
// Rewrite /images/... to /api/images/...
return NextResponse.rewrite('/api' + req.nextUrl.pathname);
}
With both these in use, visiting /images/photo.png will internally rewrite to /api/images/photo.png and in turn be handled by express.static, allowing you to serve files outside an API route and without using a custom server!
This code can surely be simplified and get rid of the need of initializing a express.js app just to handle a request, but its incredibly simple to integrate express.js into next.js without using a custom server!
I posted this question and my own answer here on StackOverflow because I was unable to find a good tutorial on how to do this. Nearly every google search says to use a custom server or to just put your files in the public folder, which is not what I was looking for. Hopefully, others who are looking for the same thing may find it here.
The public folder can only serve those files that were included at build time.
But we can do some workaround that can serve files that were not included at build time.
Solution starts here
We can create an api endpoint. For example /api/images-endpoint/[...slug].js
import fs from "fs";
import path from "path";
export default function handler(req, res) {
const imagePath = req.query.slug.join("/");
const filePath = path.resolve(".", `images-directory/${imagePath}`);
const imageBuffer = fs.readFileSync(filePath);
res.setHeader("Content-Type", "image/jpg");
return res.send(imageBuffer);
}
By this, our endpoint will read the image from the image directory and send it as a response.
Benifit/Note: This solution works for images that were added after Next project is build i-e npm run build or next build
Drawback: Using this, We can not build optimized images in Next JS Image component i-e next/image

NextJS API route conflict

I have been transitioning to NextJS from CRA. I'm re-implementing authentication using SSR, and I would like to use NextJS built-in API routes to set HTTPOnly cookies.
There is a weird problem where my backend base URL is https://somesite.com/api and my frontend's - https://somesite.com/. This means that NextJS built-in API routes will be in conflict with my own backend.
I have been researching online and couldn't find any way on changing routing in NextJS, and I don't want to change base URL of my backend, since there are other services depending on it.
Has anyone had the same issue? How can I resolve this issue?
Try next.js rewrite
Rewrites allow you to map an incoming request path to a different destination path. Docs
Check Live Example here: https://stackblitz.com/edit/nextjs-rewrite
If Path is /api it'll point to JSON placeholder APIs.(ex. /api/todos)
module.exports = {
async rewrites() {
return [
{
source: '/api/:slug*',
destination: 'https://jsonplaceholder.typicode.com/:slug*'
}
];
}
};

How to add authentication in static generated NuxtJS website

I built a website using NuxtJS, I used the SSR and target: 'server', but now I need to optimize my website for SEO, and SMO, so for this I need to set the target: 'static', because it's important to have server side rendered pages when sharing it in social media.
Problems
I'm using nuxtjs auth module for api authentication using Laravel Sanctum.
When I set the target: 'static', it's proxy does not work, which means it's axios fetching links are not working.
So now how to make my website SEO, and SMO friendly?

Avoiding middleware for static content in express app

I'm developing a small web app using express framework and I'm having a tough time with static content configuration.
In my app I have several custom middleware functions and I don't want the static files (css, js, images,...) to be processed by them. I tried to put static configuration as the first middleware but it doesn't fix anything.
My code is something like this:
var app = express();
app.configure(function() {
app.set('port', 3000);
app.set('views', __dirname + '/views');
app.use(express.static(path.join(__dirname, 'public')));
app.use(myCustomMiddleware());
});
I log the requests that reach my custom middleware and I can see requests to css and js (files inside public folder) getting there.
My goal is to return as soon as possible when receiving a static file request.
Any tips on this?
Thanks.
I'm closing this question as the problem is my own code.
Trying to isolate the components I found that I was modifying incoming request URL unintentionally.