jdev9487
jdev9487

Reputation: 131

CORS error when fetching image from container using docker-compose

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

Answers (1)

jdev9487
jdev9487

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

Related Questions