Next.js Discord

Discord Forum

Returning Dockerode container ID without using var (which also causes it to return the wrong one)

Answered
!=tgt posted this in #help-forum
Open in Discord
I'm using App Router, and in a bit of a pickle. I have the following code:
var containerId: string;
export async function PUT(req: NextRequest) {
  const protocol = process?.env.SSL === "true" ? "https" : "http";
  const config = await fetch(
    `${protocol}://${req.headers.get("host")}/api/config`,
  ).then((res) => res.json());
  const configImages: Image[] = await config.images;
  const { image } = await req.json();
  if (!configImages.find((img) => img.dockerImage === image)) {
    return Response.json(
      { error: "Image not allowed", success: false },
      { status: 400 },
    );
  } else {
    await docker.pull(image, (err: any, stream: NodeJS.ReadableStream) => {
      if (err) {
        return Response.json({ error: err, success: false }, { status: 500 });
      }
      docker.modem.followProgress(stream, onFinished);
      async function onFinished() {
        const container = await docker.createContainer({
          Image: image,
          name: Date.now() + "-" + image.split("/")[2],
          HostConfig: {
            PortBindings: {
              "3000/tcp": [{ HostPort: "3000" }],
            },
          },
        });
        container.start();
        containerId = container.id;
      }
    });
    return Response.json({ id: containerId, success: true }, { status: 201 });
  }
}

This code has 2 issues.
1. using var is not a good practice, and using let wouldn't work
2. Something to do with using var is also making Dockerode return the wrong ID.
Any ways to fix this?
Answered by josh
oh, i see. onFinished is a callback so sits outside of being awaited. try wrapping it in a promise like this

export async function PUT(req: NextRequest) {
  const protocol = process?.env.SSL === "true" ? "https" : "http";
  const config = await fetch(
    `${protocol}://${req.headers.get("host")}/api/config`
  ).then((res) => res.json());
  const configImages: Image[] = await config.images;
  const { image } = await req.json();
  if (!configImages.find((img) => img.dockerImage === image)) {
    return Response.json(
      { error: "Image not allowed", success: false },
      { status: 400 }
    );
  } else {
    try {
      const containerId = await new Promise((resolve, reject) => {
        docker.pull(image, (err: any, stream: NodeJS.ReadableStream) => {
          if (err) {
            return reject(err);
          }
          docker.modem.followProgress(stream, onFinished);
          async function onFinished() {
            const container = await docker.createContainer({
              Image: image,
              name: Date.now() + "-" + image.split("/")[2],
              HostConfig: {
                PortBindings: {
                  "3000/tcp": [{ HostPort: "3000" }],
                },
              },
            });
            container.start();
            resolve(container.id);
          }
        });
      });

      return Response.json({ id: containerId, success: true }, { status: 201 });
    } catch (e) {
      return Response.json(
        { error: e as any, success: false },
        { status: 500 }
      );
    }
  }
}
View full answer

11 Replies

Try this?

docker.modem.followProgress(stream, onFinished.bind(this));

otherwise, onFinished is bound within a different lexical scope
@josh Try this? `docker.modem.followProgress(stream, onFinished.bind(this))`; otherwise, `onFinished` is bound within a different lexical scope
This sent me into a bit of a rabbithole, and I tried doing arrow functions (which would be the same as using this), but that also does not work
yes exactly!
if it fixed the problem, don't forget to mark as solution with Right Click > Apps > Mark Solution
@josh yes exactly!
i said it does not work
oh, i misread
why isn't containerId inside the PUT method
@josh why isn't containerId inside the PUT method
does this for some reason
oh, i see. onFinished is a callback so sits outside of being awaited. try wrapping it in a promise like this

export async function PUT(req: NextRequest) {
  const protocol = process?.env.SSL === "true" ? "https" : "http";
  const config = await fetch(
    `${protocol}://${req.headers.get("host")}/api/config`
  ).then((res) => res.json());
  const configImages: Image[] = await config.images;
  const { image } = await req.json();
  if (!configImages.find((img) => img.dockerImage === image)) {
    return Response.json(
      { error: "Image not allowed", success: false },
      { status: 400 }
    );
  } else {
    try {
      const containerId = await new Promise((resolve, reject) => {
        docker.pull(image, (err: any, stream: NodeJS.ReadableStream) => {
          if (err) {
            return reject(err);
          }
          docker.modem.followProgress(stream, onFinished);
          async function onFinished() {
            const container = await docker.createContainer({
              Image: image,
              name: Date.now() + "-" + image.split("/")[2],
              HostConfig: {
                PortBindings: {
                  "3000/tcp": [{ HostPort: "3000" }],
                },
              },
            });
            container.start();
            resolve(container.id);
          }
        });
      });

      return Response.json({ id: containerId, success: true }, { status: 201 });
    } catch (e) {
      return Response.json(
        { error: e as any, success: false },
        { status: 500 }
      );
    }
  }
}
Answer
nice 🙂