File uploads
Build an avatar endpoint: accept an image, validate its size and type, store it, and serve it back.
1. Accept an upload
Point uploads at a folder, and file fields on a multipart form land on ctx.body:
import server from '@server/next';
export default server({ uploads: './uploads' })
.post('/avatar', (ctx) => {
console.log(ctx.body.avatar);
// { id, name, path, type, size }
return 201;
});Files stream to storage as they arrive, so they are never buffered whole in memory.
2. Validate size and type
Swap the folder for the object form to reject files before your handler runs:
export default server({
uploads: {
bucket: './uploads',
maxSize: '5mb',
fileType: ['image/jpeg', 'image/png'],
},
})
.post('/avatar', (ctx) => ({ id: ctx.body.avatar.id }));An oversized or wrong-type file is rejected automatically, the handler never runs.
3. Serve it back
Return a stored file from a route and it streams back with the right Content-Type. Guard it however you like first, an auth check, an ownership check:
export default server({ uploads: './uploads' })
.get('/avatar/:id', (ctx) => {
// if (!ctx.user) return 401;
return ctx.options.uploads.file(ctx.url.params.id);
});A missing file returns 404 for you. See Serving stored files for signed URLs and cloud buckets.
Next steps
- Store files in an S3-compatible bucket for production.
- Accept several files by repeating the field name,
ctx.body.photosbecomes an array.