Skip to content

Commit 9e17f2c

Browse files
authored
Merge pull request #1269 from joshunrau/fix-build
use msgpackr to encode exports as optimized buffer rather than massive JSON
2 parents c9d1a22 + 9261118 commit 9e17f2c

File tree

10 files changed

+180
-9
lines changed

10 files changed

+180
-9
lines changed

apps/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
"@prisma/client": "catalog:",
4444
"axios": "catalog:",
4545
"express": "^5.0.1",
46+
"fastify": "^5.7.1",
4647
"lodash-es": "workspace:lodash-es__4.x@*",
4748
"mongodb": "^6.15.0",
49+
"msgpackr": "catalog:",
4850
"neverthrow": "catalog:",
4951
"passport": "^0.7.0",
5052
"passport-jwt": "^4.0.1",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Injectable } from '@nestjs/common';
2+
import type { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
3+
import type { FastifyReply } from 'fastify';
4+
import { Packr } from 'msgpackr';
5+
import { Observable } from 'rxjs';
6+
import { map } from 'rxjs/operators';
7+
8+
@Injectable()
9+
export class MsgpackInterceptor implements NestInterceptor {
10+
private packr = new Packr({
11+
useRecords: true
12+
});
13+
14+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
15+
const response = context.switchToHttp().getResponse<FastifyReply>();
16+
17+
return next.handle().pipe(
18+
map((data) => {
19+
response.header('Content-Type', 'application/x-msgpack');
20+
return this.packr.pack(data);
21+
})
22+
);
23+
}
24+
}

apps/api/src/instrument-records/instrument-records.controller.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
/* eslint-disable perfectionist/sort-classes */
22

33
import { CurrentUser, ParseSchemaPipe, ValidObjectIdPipe } from '@douglasneuroinformatics/libnest';
4-
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from '@nestjs/common';
4+
import {
5+
Body,
6+
Controller,
7+
Delete,
8+
Get,
9+
HttpCode,
10+
HttpStatus,
11+
Param,
12+
Patch,
13+
Post,
14+
Query,
15+
UseInterceptors
16+
} from '@nestjs/common';
517
import { ApiOperation, ApiTags } from '@nestjs/swagger';
618
import type { InstrumentKind } from '@opendatacapture/runtime-core';
719
import {
@@ -13,6 +25,7 @@ import { z } from 'zod/v4';
1325

1426
import type { AppAbility } from '@/auth/auth.types';
1527
import { RouteAccess } from '@/core/decorators/route-access.decorator';
28+
import { MsgpackInterceptor } from '@/core/interceptors/msgpack.interceptor';
1629

1730
import { InstrumentRecordsService } from './instrument-records.service';
1831

@@ -67,6 +80,7 @@ export class InstrumentRecordsController {
6780
@ApiOperation({ summary: 'Export Records' })
6881
@Get('export')
6982
@RouteAccess({ action: 'read', subject: 'InstrumentRecord' })
83+
@UseInterceptors(MsgpackInterceptor)
7084
exportRecords(@CurrentUser('ability') ability: AppAbility, @Query('groupId') groupId?: string) {
7185
return this.instrumentRecordsService.exportRecords({ groupId }, { ability });
7286
}

apps/api/src/instrument-records/instrument-records.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ export class InstrumentRecordsService {
132132
return this.instrumentRecordModel.exists(where);
133133
}
134134

135-
async exportRecords({ groupId }: { groupId?: string } = {}, { ability }: Required<EntityOperationOptions>) {
135+
async exportRecords(
136+
{ groupId }: { groupId?: string } = {},
137+
{ ability }: Required<EntityOperationOptions>
138+
): Promise<InstrumentRecordsExport> {
136139
const records = await this.queryRecordsRaw(ability, groupId);
137140

138141
const instrumentIds = new Set(records.map((r) => r.instrumentId));

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"lodash-es": "workspace:lodash-es__4.x@*",
4646
"lucide-react": "^0.507.0",
4747
"motion": "catalog:",
48+
"msgpackr": "catalog:",
4849
"papaparse": "workspace:papaparse__5.x@*",
4950
"qrcode": "^1.5.4",
5051
"react": "workspace:react__19.x@*",

apps/web/src/routes/_app/datahub/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { removeSubjectIdScope } from '@opendatacapture/subject-utils';
1010
import { createFileRoute, useNavigate } from '@tanstack/react-router';
1111
import axios from 'axios';
1212
import { UserSearchIcon } from 'lucide-react';
13+
import { unpack } from 'msgpackr/unpack';
1314
import { unparse } from 'papaparse';
1415

1516
import { IdentificationForm } from '@/components/IdentificationForm';
@@ -50,12 +51,13 @@ const Toggles: React.FC<{ table: TanstackTable.Table<Subject> }> = ({ table }) =
5051
};
5152

5253
const getExportRecords = async () => {
53-
const response = await axios.get<InstrumentRecordsExport>('/v1/instrument-records/export', {
54+
const response = await axios.get<ArrayBuffer>('/v1/instrument-records/export', {
5455
params: {
5556
groupId: currentGroup?.id
56-
}
57+
},
58+
responseType: 'arraybuffer'
5759
});
58-
return response.data;
60+
return unpack(new Uint8Array(response.data)) as InstrumentRecordsExport;
5961
};
6062

6163
const handleExportSelection = (option: 'CSV' | 'Excel' | 'JSON') => {

apps/web/src/services/axios.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ axios.defaults.baseURL = config.setup.apiBaseUrl;
1010
axios.interceptors.request.use((config) => {
1111
const accessToken = useAppStore.getState().accessToken;
1212

13-
config.headers.setAccept('application/json');
13+
config.headers.setAccept(['application/json', 'application/x-msgpack']);
1414

1515
// Do not set timeout for setup (can be CPU intensive, especially on slow server)
1616
if (

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"@tailwindcss/oxide",
8888
"esbuild",
8989
"mongodb-memory-server",
90+
"msgpackr-extract",
9091
"msw",
9192
"oxc-resolver",
9293
"prisma",

pnpm-lock.yaml

Lines changed: 126 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ catalog:
2020
esbuild: '0.23.x'
2121
esbuild-wasm: '0.23.x'
2222
happy-dom: '^20.0.2'
23+
msgpackr: ^1.11.8
2324
motion: '11.15.0'
2425
neverthrow: ^8.2.0
2526
nodemon: '^3.1.9'

0 commit comments

Comments
 (0)