Now that we have a good overview of what the example is app is about, let's see how the various focus points are implemented! In this chapter, we'll focus on file downloads, both for the (public) promo images and the file deliverables. The latter is the more critical one, as that involves access control: only users who bought the product should be able to download it.
Let's start with the easier of the two, the promo images!
When the client fetches the product item the backend returns the image URL as well:
paths:
/product/{id}:
get:
summary: Get a product by id
# ...
responses:
'200':
description: The product
content:
application/json:
schema:
$ref: '#/components/schemas/Product'
components:
schemas:
Product:
type: object
properties:
image:
type: string
description: Promo image URL
example: /api/product/product1/image
# ...
This points to a public endpoint, no authentication needed. Because of this, it can be used as an img src
:
<img class="object-fit-contain" src=${product.image}/>
On the backend, there is a path for returning the image bytes for a product image:
paths:
/product/{id}/image:
get:
security: []
summary: Download the product image
# ...
responses:
'200':
description: successful operation
content:
'image/*':
schema:
type: string
format: binary
Behind the API, the implementation reads the file from the filesystem and writes the contents to the response:
import stream from "node:stream/promises";
const product = await db.get(
"SELECT * FROM Product WHERE id = :id",
{":id": c.request.params.id}
);
res.status(200)
.type(product.image_content_type)
.set("Cache-Control", "no-cache");
await stream.pipeline(
fs.createReadStream(path.join(
__dirname, "images", product.image
)),
res,
)