Reputation: 131
I have a NextJS frontend blog app that retrieves images and videos from a separate animations API. When this is all being run locally (frontend blog at localhost:3000 and animations API at localhost:8001) everything works as expected and the images (obtained from the animations API) are rendered.
The issue arises when I containerize the app and use docker-compose. This is my compose.yaml
file:
version: '3.8'
services:
blog-app:
build:
context: . # <-- ENSURES REBUILD EVERY TIME
dockerfile: Dockerfile
environment:
- ANIMATION_URL=http://animation-app:5000 # <-- RELEVANT
- MARKDOWN_URL=http://markdown-app:5000
- AVATARS_URL=https://avatars.githubusercontent.com
nginx:
image: jdev9487/blog-proxy-server:latest
depends_on:
- blog-app
ports:
- '443:443'
- '80:80'
volumes:
- ./.nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
animation-app: # <-- RELEVANT
image: jdev9487/math-blog-animations:latest
ports:
- '8001:5000'
markdown-app:
image: jdev9487/blog-markdown:latest
ports:
- '8000:5000'
The relevant fetch
call in the blog app is this:
"use client";
import Link from "next/link";
import Avatar from '@mui/material/Avatar';
import { PostMetadata } from "./postMetadata";
import ShareIcon from '@mui/icons-material/Share';
import { useEffect, useState } from "react";
export default function PostPreview(props: PostMetadata) {
const [img, setImg] = useState('')
useEffect(() => {
const fetchData = async () => {
const url = `${props.animationUrl}/thumbnails/${props.featuredAnimation}`;
// props.animationUrl gets populated from env variables in compose.yaml
var res = await fetch(url);
var blob = await res.blob();
var blobUrl = URL.createObjectURL(blob);
setImg(blobUrl);
}
fetchData();
}, []);
return (
<div className="flex flex-row border border-background-secondary shadow min-h-80">
<div className="flex basis-1/2">
<img id="thumbnail" className="object-cover" src={img} />
The animation API is a python flask app:
from flask import Flask
from flask import send_file
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.get("/animations/<slug>")
def animation(slug):
filename = f'output/{slug}.mp4'
return send_file(filename, mimetype='image/mp4')
@app.get("/thumbnails/<slug>") # <-- this is the endpoint being called
def thumbnail(slug):
filename = f'output/{slug}.png'
return send_file(filename, mimetype='image/png')
if __name__ == "__main__":
app.run()
I now, somewhat understandably, run into CORS issues. In contrast to the non-containerised setup, API calls are now attempting to fetch images from a different origin; this is the console error in the browser from the blog app:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://animation-app:5000/thumbnails/completingTheSquare. (Reason: CORS request did not succeed). Status code: (null)
My question is: why do I get CORS issues?
I have included CORS in the animations API so would expect that, according to docs, all domains and all routes would be allowed. I'm not sure if I need to amend the fetch parameters from the client calling the API...
I don't have a strong enough grasp on CORS in general to understand what the issue is here.
Upvotes: 1
Views: 77
Reputation: 131
As highlighted by @DavidMaze and @willwrighteng, the issue was resolved by employing the already existing nginx container (jdev9487/blog-proxy-server:latest
) as a reverse proxy server.
All CORS was removed from the animations API. Additional config was placed in the default.conf
for the nginx server:
...
location /animation/ {
proxy_pass http://animation-app:5000/;
}
...
The compose.yaml
file was edited:
version: '3.8'
services:
blog-app:
build:
context: .
dockerfile: Dockerfile
environment:
- ANIMATION_URL=/animation
Now fetch
calls to the animation app first go to localhost/animation
which is the same origin and hence no CORS issues.
Upvotes: 0