Skip to content

feat: Form and multipart - #380

Open
namzug16 wants to merge 9 commits into
serverpod:mainfrom
namzug16:feat/form_and_multipart
Open

namzug16 wants to merge 9 commits into
serverpod:mainfrom
namzug16:feat/form_and_multipart

Conversation

@namzug16

Copy link
Copy Markdown

Description

In this PR I'm adding first class form parsing support to Relic, including URL-encoded forms, multipart form-data streaming, multipart aggregation, upload metadata models, temporary file upload storage (on relic_io), and related tests/examples

This makes it easier for Relic handlers to safely read submitted form fields and uploaded files without manually parsing request bodies, which is pretty useful when developing SSR applications

Related Issues

Pre-Launch Checklist

Please ensure that your PR meets the following requirements before submitting:

  • This update focuses on a single feature or bug fix. (For multiple fixes, please submit separate PRs.)
  • I have read and followed the Dart Style Guide and formatted the code using dart format.
  • I have referenced at least one issue this PR fixes or is related to.
  • I have updated/added relevant documentation (doc comments with ///), ensuring consistency with existing project documentation.
  • I have added new tests to verify the changes.
  • All existing and new tests pass successfully.
  • I have documented any breaking changes below.

Breaking Changes

  • Includes breaking changes.
  • No breaking changes.

Code examples

// Read any supported HTML form:
// - application/x-www-form-urlencoded
// - multipart/form-data
Future<Response> handleForm(Request req) async {
  try {
    //auto detects the form type
    // - application/x-www-form-urlencoded
    // - multipart/form-data
    // if the content-type is neither it throws UnsupportedFormMediaTypeException
    final form = await req.formData();

    // - application/x-www-form-urlencoded
    // final form = await req.urlEncodedForm();

    // - multipart/form-data
    // form = await req.multipartForm(
    //   uploadStorage: TempUploadStorage(directory: uploadDir),
    // );
    // You should call form.dispose() when done so temp files can be cleaned up

    // returns first value or null
    final name = form.fields.get('name');
    // if no email is provided throws a StateError, otherwise it returns the first value
    final email = form.fields.getRequired('email');
    // returns list of values or an empty list
    final tags = form.fields.getAll('tag');

    // example of a file
    // final avatar = form.files.get('avatar');

    return Response.ok(
      Body.fromString('name=$name email=$email tags=$tags'),
    );
  } on FormException catch (error) {
    return Response(
      error.statusCode,
      body: Body.fromString(error.message),
    );
  } finally {
    // await form?.dispose();
  }
}


// Stream multipart parts without aggregating the whole form.
Future<Response> handleStreamingUpload(Request req) async {
  final lines = <String>[];

  await for (final part in req.multipart()) {
    if (part.isField) {
      lines.add('field ${part.name}: ${await part.readAsString()}');
      continue;
    }

    if (part.isFile) {
      var bytes = 0;
      await for (final chunk in part.body.read()) {
        bytes += chunk.length;
      }

      lines.add(
        'file ${part.name}: filename=${part.filename}, bytes=$bytes',
      );
      continue;
    }

    await part.discard();
  }

  return Response.ok(Body.fromString(lines.join('\n')));
}

// Apply custom limits.
final form = await req.multipartForm(
  limits: const FormLimits(
    maxBodySize: 32 * 1024,
    maxFieldCount: 8,
    maxFileCount: 1,
    maxPartCount: 8,
    maxFieldSize: 64,
    maxFileSize: 1024,
    maxTotalFileSize: 1024,
    maxPartHeaderSize: 8 * 1024,
    maxBoundarySize: 200,
  ),
);

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 69fc6d07-bb4e-4605-a6c1-9aa1e7238782

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@namzug16

Copy link
Copy Markdown
Author

Hey @nielsenko, could you please review this PR? it is connected to the issue #379

Thank you!

PS. I haven't added any docs into the site because I wasn't really if I should do it, or if you guys have any sort of guidelines for new docs in the site

@nielsenko

Copy link
Copy Markdown
Collaborator

@namzug16 Thank you for your contribution - I'll get to it Wednesday next week.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.49275% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.94%. Comparing base (7054fe9) to head (4b4967b).

Files with missing lines Patch % Lines
...elic_io/lib/src/io/upload/temp_upload_storage.dart 88.88% 6 Missing ⚠️
...lic_core/lib/src/form/request_form_extensions.dart 95.65% 5 Missing ⚠️
packages/relic_core/lib/src/form/form_data.dart 94.52% 4 Missing ⚠️
...ckages/relic_core/lib/src/form/multipart_part.dart 92.59% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #380      +/-   ##
==========================================
+ Coverage   92.74%   92.94%   +0.19%     
==========================================
  Files         110      115       +5     
  Lines        4702     5047     +345     
  Branches     2380     2542     +162     
==========================================
+ Hits         4361     4691     +330     
- Misses        341      356      +15     
Flag Coverage Δ
relic_core 92.97% <95.53%> (+0.27%) ⬆️
relic_io 92.70% <88.88%> (-0.42%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nielsenko

Copy link
Copy Markdown
Collaborator

@namzug16 Looking at this now. Sorry for the delay.

@nielsenko nielsenko changed the title feat: form and multipart feat: Form and multipart Sep 25, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants